题意:在 [ 1 , A ] [ 1 , B ]分别选 x , y 使得 (x&y)>C || (x^y)<C 成立
思路:或命题 -> 正难则反
(x&y) > c || (x^y)< C 否命题为 (x&y) <= c&& (x^y)>= C
与位运算有关,则化简为二进制的数位DP问题
注意:DP 求出的解 x y 的取值为 [ 0 , A ] [ 0 , B ] ,则应该多减去 x=0和y=0 的贡献 max(A-C+1,0)+max(B-C+1,0)
数位DP理解: 在一定区间内 dfs搜索合法解,利用高位数大小 确保 数在定义域区间内 并 从高位到地位枚举。
#include<bits/stdc++.h> #define ll long long using namespace std; int A,B,C; int a[35],b[35];//A B 二进制i位 的数 ll dp[35][2][2][2][2]; ll dfs(int pos,int sta1,int sta2,int lim1,int lim2) { if(pos<0) { return 1; } if(dp[pos][sta1][sta2][lim1][lim2]!=-1) return dp[pos][sta1][sta2][lim1][lim2]; int up1 = lim1? a[pos]:1; int up2 = lim2? b[pos]:1; ll ans=0; for(int i=0;i<=up1;i++) for(int j=0;j<=up2;j++) { int x=(C>>pos)&1; if(!sta1&&(i&j)>x) continue;//非法解 if(!sta2&&(i^j)<x) continue; int sta11=sta1|| ( (i&j)<x );// (i&j)==x时 sta11=0 ((i&j)>x) 已经continue int sta22=sta2|| ( (i^j)>x ); ans+=dfs(pos-1,sta11,sta22,lim1&&(i==up1),lim2&&(j==up2)); //lim1 lim2 限制 x y 在[0,a] [0,b] 定义域 //sta1 sta2 限制 x&y<=c x^y>=C } return dp[pos][sta1][sta2][lim1][lim2]=ans; //!!!记忆化!! } int main() { int T; cin>>T; while(T--) { cin>>A>>B>>C; ll ans=1ll*A*B; ans+=max(A-C+1,0)+max(B-C+1,0);//数为dp 多算了 x,y 出现0 的贡献 int AA=A,BB=B; memset(dp, -1, sizeof(dp)); memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); int ind=0; while(AA) { a[ind++]=AA%2; AA/=2; } ind=0; while(BB) { b[ind++]=BB%2; BB/=2; } printf("%lld\n",ans-dfs(31,0,0,1,1)); // (x&y) > c || (x^y)< C 的否命题为 (x&y) <= c && (x^y)>= C //正难则反 } }