满足要求的<x,y>有两种情况:
1.x&y >C;
2.x^y < C;
其中1<=x<=A, 1<=y<=B.
涉及到位运算,我们首先把A,B,C写成二进制,其次,要明确的一点就是,我们任找某个x,y,假设我们现在在进行&运算,并且是从高位到低位运算的,运算到二进制中的第k位时,如果此时k上的数字num, num>C[k] (2进制),那么就可以判定x&y>C了,而如果num<C[k],则x&y<C,如果num==C[k],那么就进行下一位的比较。^运算也同理。因此,根据这一点,我们就可以用数位DP来做了。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
typedef long long ll;
ll dp[40][3][3][2][2][2][2];
//dp[pos][st1][st2][limit1][limit2][a][b]
//st1表示&运算的状态,2表示尚不确定与C的大小关系,1表示大于,0表示小于
//st2表示^运算的状态,2表示尚不确定与C的大小关系,1表示大于,0表示小于
//显然,我们最终要的状态是 st1==1||!st2
//a表示枚举到pos位,x是否为0,a==0表示x为0;
//b表示枚举到pos位,y是否为0,b==0表示y为0;
//这里要特别地加a,b,是因为如果把0算进去会多很多情况,但这些情况是我们不需要的。
//一开始我并不是再开了两维来记录这个a,b这个状态的,但是WA了,所以这两维是不可少的
//尚不清楚原理,但猜测是会将某些情况漏举了。反正空间也大不了多少,DP状态越具体的话越不容易错,就加上呗。
int A[40],B[40],C[40];
ll dfs(int pos,int st1,int st2,int a,int b,bool limit1,bool limit2)
{
if(pos==-1)
{
if((st1==1||!st2)&&a&&b) return 1;
return 0;
}
if(dp[pos][st1][st2][limit1][limit2][a][b]!=-1) return dp[pos][st1][st2][limit1][limit2][a][b];
ll ret=0;
int up1= limit1? A[pos]:1;
int up2= limit2? B[pos]:1;
for(int i=0;i<=up1;++i) //是在[1,A],[1,B]两个区间进行枚举的,所以再加一个循环就可以,可以推出n个区间就用n层循环
for(int j=0;j<=up2;++j)
{
int t1=i&j, t2=i^j;
int temp1=st1,temp2=st2;
if(st1==2&&t1>C[pos]) temp1=1; //我们只需要分析st1==2时的情况,因为st1=1,0这两种情况结果都已经确定了,不用再分析,下同
else if(st1==2&&t1<C[pos]) temp1=0;
if(st2==2&&t2<C[pos]) temp2=0;
else if(st2==2&&t2>C[pos]) temp2=1;
ret+=dfs(pos-1,temp1,temp2,a|i,b|j,limit1&&i==A[pos],limit2&&j==B[pos]);
}
dp[pos][st1][st2][limit1][limit2][a][b]=ret;
return ret;
}
ll solve(ll a,ll b,ll c)
{
int pos1=0,pos2=0,pos3=0,pos;
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
memset(C,0,sizeof(C));
while(a) A[pos1++]=a%2, a/=2;
while(b) B[pos2++]=b%2, b/=2;
while(c) C[pos3++]=c%2, c/=2;
memset(dp,-1,sizeof(dp));
pos=max(pos1,max(pos2,pos3));
return dfs(--pos,2,2,0,0,1,1);
}
int main()
{
int T;
cin>>T;
while(T--)
{
ll a,b,c;
cin>>a>>b>>c;
cout<<solve(a,b,c)<<endl;
}
return 0;
}
小结:
1.在n个区间里面枚举,就用n层循环DP;
2.a,b,这两维要加上,状态是越具体越不容易出错的(个人经验)
3.本来,dp数组里面是没有[limit1][limit2]的,是在dfs里加了个! limit1&&! limit2的判断,结果TLE!!!加上去以后5MS秒过!!!(神奇)所以以后或许可以用这一定来优化一下时间复杂度。