这题除了有个细节都蛮简单(我卡了几个小时…)

求 ( a , b ) (a,b) (a,b)的对数

可以直接 d p [ l e n ] [ q ] [ w ] dp[len][q][w] dp[len][q][w]表示 a & b a\&b a&b是否大于C以及 a ⊕ b a\oplus b a⊕b是否小于 C C C

这样就枚举DP

枚举过程中有三种状态

0 表 示 目 前 无 法 确 定 大 小 关 系 0表示目前无法确定大小关系 0表示目前无法确定大小关系

1 表 示 已 经 满 足 1表示已经满足 1表示已经满足

2 表 示 再 也 无 法 满 足 2表示再也无法满足 2表示再也无法满足

我开始没有考虑状态 2 2 2找 b u g bug bug找疯了

而且这里 a 和 b a和b a和b不能为 0 0 0

所以需要减掉当 a = 0 , b 取 [ 1 , m i n ( c − 1 , b ) ] a=0,b取[1,min(c-1,b)] a=0,b取[1,min(c−1,b)]都是满足条件的

同 理 得 b = 0 , 以 及 a , b 都 为 0 得 情 况 同理得b=0,以及a,b都为0得情况 同理得b=0,以及a,b都为0得情况

这里不能在递归边界强制要求a,b要不等于1

因为 这 是 一 个 d p 转 移 的 过 程 , 后 续 要 用 到 这 个 d p 值 这是一个dp转移的过程,后续要用到这个dp值 这是一个dp转移的过程,后续要用到这个dp值

后 续 在 高 位 填 了 1 , 然 后 发 现 低 位 d p 值 计 算 过 了 后续在高位填了1,然后发现低位dp值计算过了 后续在高位填了1,然后发现低位dp值计算过了

讲 道 理 可 以 直 接 把 后 续 全 部 为 0 的 情 况 拿 过 来 用 , 但 之 前 没 计 算 , 所 以 是 错 的 讲道理可以直接把后续全部为0的情况拿过来用,但之前没计算,所以是错的 讲道理可以直接把后续全部为0的情况拿过来用,但之前没计算,所以是错的

#include <bits/stdc++.h>
using namespace std;
#define int long long
int dp[60][3][3][3][3],a[69],b[69],c[69],A,B,C;
int dfs(int len,int qlim,int wlim,int da,int xiao)
{
if( len==0 )
{
if( da!=1&&xiao!=1 ) return 0;
return 1;
}
if( dp[len][da][xiao][qlim][wlim]!=-1 )
return dp[len][da][xiao][qlim][wlim];
int q=qlim?a[len]:1,ans=0;
int w=wlim?b[len]:1;
for(int i=0;i<=q;i++)
for(int j=0;j<=w;j++)
{
int yu=(i&j),xo=(i^j),nc1=da,nc2=xiao;
if( !da )
{
if( yu<c[len] ) nc1=2;//小于,彻底废了
else if( yu>c[len] ) nc1=1;//大于符合要求
else nc1=0;//等于不能确定
}
if( !xiao )
{
if( xo>c[len] ) nc2=2;
else if( xo<c[len] ) nc2=1;
else nc2=0;
}
ans+=dfs(len-1,qlim&&(i==q),wlim&&(j==w),nc1,nc2);
}
dp[len][da][xiao][qlim][wlim]=ans;
return ans;
}
int solve(int x)
{
while( x )
{
c[++c[0]]=x&1;
x>>=1;
}
int len=max(a[0],max(b[0],c[0]));
return dfs(len,1,1,0,0);
}
signed main()
{
int t; cin >> t;
while( t-- )
{
cin >> A >> B >> C;
int now=-min(A,C-1)-min(B,C-1)-1;
for(int i=0;i<=32;i++) a[i]=b[i]=c[i]=0;
memset(dp,-1,sizeof(dp));
while( A )
{
a[++a[0]]=A&1;
A>>=1;
}
while( B )
{
b[++b[0]]=B&1;
B>>=1;
}
cout << solve(C)+now<< endl;
}
}