2019 牛客网 第七场 H pair
题目链接:https://ac.nowcoder.com/acm/contest/887/H
题意:
给定A,B,C问在[1,A]和[1,B]中有多少对x,y满足x&y>C或者x^y<C.
数据范围:1<=A,B,C<=1e9,测试用例 T<=100 .
思路:
根据题意本题可以用数位dp来解。
/* and 0&0=0; 0&1=0; 1&0=0; 1&1=1; xor 0 ^ 0=0; 0 ^ 1=1; 1^ 0=1; 1^1=0; 3 3 4 2 4 5 2 7 8 5 5 7 31 1<=x<=a 1<=y<=b x&y>c x^y<c */ #include <iostream> #include <bitset> using namespace std; int main() { int t; cin>>t; while(t--) { int a,b,c,ans=0; cin>>a>>b>>c; for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) { if((i^j)<c) { ans++; cout<<(i^j)<<" "; bitset<8> aa(i); bitset<8> bb(j); bitset<8> cc(i^j); cout<<aa<<" "<<i<<endl; cout<<bb<<" "<<j<<endl; cout<<cc<<" "<<(i^j)<<endl<<endl; } } cout<<ans<<endl; } return 0; } /* 7 8 5 0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 31 0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 31 0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 0 32 0 3 2 4 3 0 1 4 2 1 0 0 1 2 4 1 0 3 4 2 3 0 22 0 3 2 3 0 1 2 1 0 0 10 0 0 0 0 0 0 0 7 0 0 1 1 0 0 1 1 0 0 1 1 0 13 0 2 0 1 2 1 0 0 1 2 1 0 2 2 0 1 2 1 0 19 0 3 2 3 0 1 2 1 0 0 1 2 3 1 0 3 2 2 3 0 1 3 2 1 0 25 0 3 2 4 3 0 1 4 2 1 0 4 0 1 2 3 4 1 0 3 2 4 2 3 0 1 4 3 2 1 0 31 */ /* 00000001 00000001 0 00000010 00000010 0 00000010 00000011 1 00000011 00000010 1 00000011 00000011 0 00000100 00000100 0 00000100 00000101 1 7 */ /* 00000001 00000010 0 3 00000001 00000100 0 5 00000001 00000011 1 2 00000010 00000001 0 3 00000010 00000100 0 6 00000011 00000100 0 7 00000011 00000001 1 2 */ /* x&y>c x^y<c 2 00000001 00000001 1 0 00000010 00000010 2 0 00000010 00000011 2 1 00000011 00000010 2 1 00000011 00000011 3 0 */ /* /* 00000001 00000100 0 5 00000001 00000110 0 7 00000001 00000111 1 6 00000001 00001000 0 9 00000010 00000100 0 6 00000010 00000101 0 7 00000010 00000111 2 5 00000010 00001000 0 10 00000011 00000100 0 7 00000011 00000101 1 6 00000011 00000110 2 5 00000011 00001000 0 11 00000100 00000001 0 5 00000100 00000010 0 6 00000100 00000011 0 7 00000100 00001000 0 12 00000101 00000010 0 7 00000101 00000011 1 6 00000101 00001000 0 13 00000110 00000001 0 7 00000110 00000011 2 5 00000110 00001000 0 14 00000111 00000001 1 6 00000111 00000010 2 5 00000111 00001000 0 15 */ /* x&y>c x^y<c 00000001 00000001 1 0 00000001 00000010 0 3 00000001 00000011 1 2 00000001 00000101 1 4 00000010 00000001 0 3 00000010 00000010 2 0 00000010 00000011 2 1 00000010 00000110 2 4 00000011 00000001 1 2 00000011 00000010 2 1 00000011 00000011 3 0 00000011 00000111 3 4 00000100 00000100 4 0 00000100 00000101 4 1 00000100 00000110 4 2 00000100 00000111 4 3 00000101 00000001 1 4 00000101 00000100 4 1 00000101 00000101 5 0 00000101 00000110 4 3 00000101 00000111 5 2 00000110 00000010 2 4 00000110 00000100 4 2 00000110 00000101 4 3 00000110 00000110 6 0 00000110 00000111 6 1 00000111 00000011 3 4 00000111 00000100 4 3 00000111 00000101 5 2 00000111 00000110 6 1 00000111 00000111 7 0 */ */
大佬的解析:
题意:给你 三个数A,B,C让你从 1~A内选择一个X,1~B内选择一个Y
找出<X,Y>的对数,使得 X&Y > C || X^Y < C
平常做的数位dp都是从一个区间内找数的个数,而现在是从两个区间找数对的对数,那就在之前的三维dp上多开两维,之前是枚举一个区间上限,现在改成两个区间上限,其他的和普通的数位dp一样
注意数位dp找到的是从0开始的 0 & Y 和 X&0 一定都小于C 不需要管
但是 0 ^ Y 和 X^0 当Y 和 X 小于C时 是满足的 所以结果还需要减去 ^运算 X 或 Y 为零时的情
原文链接:https://blog.csdn.net/Daxian911/article/details/99462695
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <vector> #include <queue> using namespace std; #define ll long long #define int long long const int maxn=1e5+5; ll dp[32][3][3][2][2]; int a[32], b[32],c[32]; ll dfs1(int pos, int And, int Xor, int limit1, int limit2){ if(pos == -1) { if(And == 1 || Xor == 2) return 1; return 0; } if(dp[pos][And][Xor][limit1][limit2] != -1) return dp[pos][And][Xor][limit1][limit2]; int up1 = limit1 ? a[pos] : 1; int up2 = limit2 ? b[pos] : 1; ll ans = 0; for(int i = 0; i <= up1; i++) { for(int j = 0; j <= up2; j++) { int x = i & j, y = i ^ j; int _and = And; int _xor = Xor; if(And == 0) { if(x > c[pos]) _and = 1; else if(x < c[pos]) _and = 2; } if(Xor == 0) { if(y > c[pos]) _xor = 1; else if(y < c[pos]) _xor = 2; } ans += dfs1(pos - 1, _and, _xor, (i == up1) && limit1, limit2 &&(j == up2)); } } dp[pos][And][Xor][limit1][limit2] = ans; return ans; } signed main() { int T, A, B, C; scanf("%lld", &T); while(T--) { memset(dp, -1, sizeof(dp)); scanf("%lld%lld%lld", &A, &B, &C); int i = 0, j = 0, k = 0; int ans = min(C - 1, A) + min (C - 1, B) + 1; while(i < 30) { a[i++] = (A & 1); A>>=1; } while(j < 30) { b[j++] = (B & 1); B>>=1; } while(k < 30) { c[k++] = (C & 1); C>>=1; } printf("%lld\n", dfs1(29,0,0,1,1) - ans); } return 0; }
另外一个大佬的代码解析:
思路:数位dp,定义一个dp[len][lim1][lim2][ok1][ok2][za][zb]。
len代表当前枚举的二进制位,lim1,lim2分别代表x和y的上限,
ok1代表对于x&y>C是否成立,成立是1,有可能成立是0,不可能成立是-1,
ok2代表对于x^y<C是否成立,1代表不成立,0代表可能成立,-1代表成立,
za和zb分别代表x和y的数中是否出现了1,因为x和y的二进制位不能是全零。转移的话代码中非常容易看懂。
原文链接:https://blog.csdn.net/qq_43316231/article/details/98968980
#include<bits/stdc++.h> using namespace std; typedef long long ll; int t; ll A,B,C,f[50],g[50],h[50]; ll dp[66][2][2][3][3][2][2]; ll dfs(ll len,ll lim1,ll lim2,ll ok1,ll ok2,ll za,ll zb){ ll &ret=dp[len][lim1][lim2][ok1+1][ok2+1][za][zb]; if(ret!=-1)return ret; if(len==0){ ret=za&&zb&&(ok1>0||ok2<0); return ret; } ret=0; --len; int up1=lim1?1:f[len]; int up2=lim2?1:g[len]; for(int x=0;x<=up1;x++)for(int y=0;y<=up2;y++) ret+=dfs(len,lim1|(x<f[len]),lim2|(y<g[len]),ok1==0?(x&y)-h[len]:ok1,ok2==0?(x^y)-h[len]:ok2,za|x,zb|y); return ret; } int main(){ scanf("%d",&t); while(t--){ memset(dp,-1,sizeof(dp)); scanf("%lld%lld%lld",&A,&B,&C); for(int i=0;i<30;i++){ f[i]=A%2,A/=2; g[i]=B%2,B/=2; h[i]=C%2,C/=2; } printf("%lld\n",dfs(31,0,0,0,0,0,0)); } return 0; }
结合大佬的
我自己水了一遍,嘻嘻
/* and 0&0=0; 0&1=0; 1&0=0; 1&1=1; xor 0 ^ 0=0; 0 ^ 1=1; 1^ 0=1; 1^1=0; 3 4 2 5 2 8 5 7 1<=x<=a 1<=y<=b 条件 x&y>c x^y<c 分析得 只满足x^y<c 即可。 a,b,c<1e9 转换为二进制数位是30个。 */ #include<bits/stdc++.h> using namespace std; typedef long long LL; LL dp[32][3][3][2][2], a[32],b[32],c[32]; LL dfs(LL len, LL And, LL Xor, LL limit1, LL limit2){ if(len==-1) { if(And == 1 || Xor == 2) return 1ll; return 0; } //满足条件,直接返回最底层答案。 if(dp[len][And][Xor][limit1][limit2] != -1) return dp[len][And][Xor][limit1][limit2]; //没到达上界并且数位已经统计过,直接返回数量。 LL up1 = limit1 ? a[len] : 1; LL up2 = limit2 ? b[len] : 1; LL cnt =0; for(LL i = 0; i <= up1; i++) { for(LL j = 0; j <= up2; j++) { LL x = i & j, y = i ^ j; LL _and = And; LL _xor = Xor; if(And == 0) { if(x > c[len]) _and = 1; else if(x < c[len]) _and = 2; } if(Xor == 0) { if(y > c[len]) _xor = 1; else if(y < c[len]) _xor = 2; } cnt += dfs(len-1,_and,_xor,(i == up1) && limit1,limit2 &&(j==up2)); } } dp[len][And][Xor][limit1][limit2]=cnt; return cnt; } LL solve(LL aa,LL bb,LL cc) { LL ans=min(cc - 1, aa) + min (cc - 1, bb) + 1; for(LL i=0;i<30;i++){ a[i]=(aa & 1); aa>>=1; b[i]=(bb & 1); bb>>=1; c[i]=(cc & 1); cc>>=1; //b[i]=bb%2,bb/=2; //c[i]=cc%2,cc/=2; } return (dfs(29,0,0,1,1)-ans); } int main() { int t; cin>>t; while(t--) { memset(dp, -1, sizeof(dp)); LL a,b,c; cin>>a>>b>>c; cout<<solve(a,b,c)<<endl; } return 0; }