CodeForces 449D(容斥,DP
题目:给出N(<=10^6)个数,每个数<=10^6. 从中选出至少1个数,易知有2^N-1种不同方案。那么在这些方案中,有多少种满足选出的所有数AND起来为0呢?
我是不会做的,找了一个题解然而看了好久看不懂,后来看了下官方题解发现我看得那个有个地方写错了。。。。
官方题解如下:
Firstly, we can use inclusion-exclusion principle in this problem. Let f(x) be the count of number i where Ai&x = x. Let g(x) be the number of 1 in the binary respresentation of x. Then the answer equals to .
Now the task is to calculate f(x) for every integer x between 0 and 220. Let fk(x) be the count of number i where Y0&X0 = X0 and X1 = Y1 (they are defined below).
We divide x and Ai into two parts, the first k binary bits and the other 20 - k binary bits. Let X0 be the first part of x and X1 be the second part of x. Let Y0 be the first part of Ai and Y1 be the second part of Ai.
We can calculate fk(x) in O(1):
The problem can be solved in O(n * 2n) now (n = 20 in this problem).
做的时候作死把pow函数里的乘数写成了int。。。。总是犯这种错误。。。我发誓以后不用int做数学题了。。。。然后,对于这个dp的思路究竟是咋想出来的还是没有啥头绪,估计下次碰到这种提还是不会吧。。。每次都能看懂题解然后下次又不会了啊。。。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<functional> #include<queue> using namespace std; typedef long long ll; const int maxv=1e5+30; const ll mod=1000000007; int N; int a[1<<20]; int dp[22][1<<20]; int fpow(int x,ll p){ int ans=1; ll xx=x; while(p>0){ if(p&1) ans=(xx*ans)%mod; xx=(xx*xx)%mod; p>>=1; } return ans; } void add(int &a,int b){ a=((a+b)%mod+mod)%mod; } int main(){ ///freopen("in","r",stdin); cin>>N; for(int i=0;i<N;i++) scanf("%d",&a[i]); sort(a,a+N); for(int i=0;i<N;i++){ if(a[i]&1) add(dp[0][a[i]],1),add(dp[0][a[i]-1],1); else add(dp[0][a[i]],1); } for(int i=1;i<20;i++){ for(int j=1;j<(1<<20);j++){ if(j&(1<<i)) add(dp[i][j],dp[i-1][j]); else add(dp[i][j],dp[i-1][j]),add(dp[i][j],dp[i-1][j+(1<<i)]); } } int ans=fpow(2,N)-1; for(int j=1;j<(1<<20);j++){ int bb=__builtin_popcount(j); if(bb%2) add(ans,-fpow(2,dp[19][j])+1); else add(ans,fpow(2,dp[19][j])-1); } cout<<ans<<endl; return 0; }