题意:
给出A、B、C,求
x
&
y
>
C
x \& y > C
x&y>C或
x
y
<
C
x ^ y < C
xy<C的(x,y)对数(
1
<
=
x
<
=
A
,
1
<
=
y
<
=
B
1<=x<=A,1<=y<=B
1<=x<=A,1<=y<=B)
思路:
一开始不知道怎么做,以前只做过一个数的数位dp,没想到两个数也可以做。
正着想的话就会有三种情况,倒着想的话只要总的减去x&y<C&&x^y>C就好了。
定义
d
p
[
p
o
s
]
[
l
i
m
a
]
[
l
i
m
b
]
[
a
n
d
]
[
x
o
r
]
[
z
a
]
[
z
b
]
dp[pos][lima][limb][and][xor][za][zb]
dp[pos][lima][limb][and][xor][za][zb]
pos:当前到第几位
lima:数字x是否达到上限
limb:数字y是否达到上限
and:x和y与运算是否小于C
xor:x和y亦或运算是否大于C
za:x是否出现过非0的数字
zb:y是否出现过非0的数字
从高位枚举,如果之前x和y相与已经小于C了,那么后面都会小于C。同样如果之前x和y亦或已经大于C了,那么之后都会一直大于C。注意x和y中有一个是全0都是不合法的情况。
最后A*B减去数位dp求出来的值就是最后的答案了。
参考代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn =50;
bool a[maxn];
bool b[maxn];
bool c[maxn];
ll dp[50][2][2][2][2][2][2];
ll dfs(int pos, bool lima,bool limb,bool ad,bool xr,bool za,bool zb){
if(pos<0){
return za&&zb;
}
if(dp[pos][lima][limb][ad][xr][za][zb]!=-1)return dp[pos][lima][limb][ad][xr][za][zb];
int topa=lima?a[pos]:1;
int topb=limb?b[pos]:1;
ll tmp=0;
for(int i=0;i<=topa;i++){
for(int j=0;j<=topb;j++){
if(!ad&&((i&j)>c[pos]))continue;
if(!xr&&((i^j)<c[pos]))continue;
tmp+=dfs(pos-1,lima&&(i==topa),limb&&(j==topb),ad||((i&j)<c[pos]),xr||((i^j)>c[pos]),za||i,zb||j);
}
}
return dp[pos][lima][limb][ad][xr][za][zb]=tmp;
}
int main(){
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++){
memset(dp,-1, sizeof(dp));
int aa,bb,cc;
scanf("%d%d%d",&aa,&bb,&cc);
ll ans=1ll*aa*bb;
//printf("%lld\n",ans);
for(int i=0;i<=30;i++){
a[i]=aa&1;
aa>>=1;
}
for(int i=0;i<=30;i++){
b[i]=bb&1;
bb>>=1;
}
for(int i=0;i<=30;i++){
c[i]=cc&1;
cc>>=1;
}
printf("%lld\n",ans-dfs(30, true,true, false,false,false,false));
}
return 0;
}