题意:给你三个数,A,B,C,计算 有多少对数,满足 x & y > c || x ^ y < c ,其中(1 <= x <= A ,1 <= y <= B)。
思路:我们可以首先求出他的反面,即有多少对数满足 【 x & y <= c && x ^ y >= c 】然后拿总数 A*B 减去它就是最终的答案,但是我数位dp枚举的是0-A,0-B的合法数目,而题目要求的是1-A,1-B的答案,所以相当于我多减去非法答案,那么最终答案就要加上当a等于0,b等于0的合法贡献。那么,我们怎么求出满足反面的总数呢?首先把 A,B化成二进制,存到数组a,b中,设dp方程为 dp【pos】【sta1】【sta2】【limit1】【limit2】分别代表,枚举到第pos位。sta1 = 0 表示当前的(x & y) = c,1表示(x & y) < c,sta2 = 0表示当前的(x ^ y) = c,1表示(x ^ y) > c 。如果枚举到第pos位,他的sta1 = 0 && (x&y)> c,那么就continue,如果它的sta1=1,或者当前位置(x & y < c ),那么sta1=1;同理sta2。
时间复杂度:状态数*转移数=32*2*2*2*2*2。(只有0和1两种状态)。
#include<bits/stdc++.h>
using namespace std;
long long d[32][2][2][2][2];
int a[32],b[32];
int C;
long long dfs(int pos,int sta1,int sta2,int limit1,int limit2)
{
if(pos==0)return 1;
if(d[pos][sta1][sta2][limit1][limit2]!=-1)
return d[pos][sta1][sta2][limit1][limit2];
int up1=limit1?a[pos]:1;
int up2=limit2?b[pos]:1;
long long ans=0;
for(int i=0;i<=up1;i++)
for(int j=0;j<=up2;j++)
{
int x=0;
if((C >> (pos-1)) & 1)x=1;
if(((i&j)>x) && sta1==0)continue;
if(((i^j)<x) && sta2==0)continue;
int t1= sta1 || (i&j)<x;
int t2= sta2 || (i^j)>x;
ans+=dfs(pos-1,t1,t2,limit1&&(i==up1),limit2&&(j==up2));
}
return d[pos][sta1][sta2][limit1][limit2]=ans;
}
int main()
{
int T;
cin>>T;
int x,y;
while(T--)
{
memset(d,-1,sizeof(d));
for(int i=0;i<=30;i++)a[i]=b[i]=0;
scanf("%d%d%d",&x,&y,&C);
long long ans = 1ll*x*y + max(x-C+1,0) + max(y-C+1,0);
int t=0;
while(x) //求出 x 的数位
{
a[++t]=x%2;
x/=2;
}
t=0;
while(y) //求出 y 的数位
{
b[++t]=y%2;
y/=2;
}
ans-=dfs(30,0,0,1,1);
printf("%lld\n",ans);
}
}