Codeforces 449D. Jzzhu and Numbers
这个显然考虑容斥,那么答案就是总方案数减去至少有一个 $1$ 的方案数加上至少有两个 $1$ 的方案数减去至少有三个 $1$ 的方案数...
即 $ans=\sum_{x=0}^{max(a)}(-1)^{cnt[x]}(2^{f[x]}-1)$ ,其中 $cnt[x]$ 表示 $x$ 二进制下 $1$ 的个数,$f[x]$ 表示 $a_{i} \text{ and } x=x$ 的 $i$ 的数量
显然 $2^{f[x]}-1$ 意思就是那些至少有 $cnt[x]$ 个 $1$ 的数任意选一些取交集的方案数
那么现在问题就是如何计算 $f[x]$ ,考虑设 $f[i][x]$ 表示只考虑前 $i-1$ 位时, $a_{i} \text{ and } x=x$ 并且后 $i$ 位中 $a_{i}=x$ 的 $i$ 的数量
那么就可以 $dp$ 了,如果 $f[i][x]$ 的第 $i$ 位为 $0$ ,那么 $f[i][x]=f[i-1][x]+f[i-1][x|(1<<i)]$
如果 $x$ 的第 $i$ 位为 $1$ ,那么 $f[i][x]=f[i-1][x]$
显然初始 $f[0][x]$ 为 $a[i]=x$ 的 $i$ 的数量
然后 $f[21][i]$ 就是我们要的结果了
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef unsigned int uint; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e6+7,mo=1e9+7; inline int fk(int x) { return x>=mo ? x-mo : x; } int n,m,a[N],ans; int f[22][(1<<21)]; inline int ksm(int x,int y) { int res=1; while(y) { if(y&1) res=1ll*res*x%mo; x=1ll*x*x%mo; y>>=1; } return res; } int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(),m=max(m,a[i]); for(int i=1;i<=n;i++) f[0][a[i]]++; for(int i=1;i<=21;i++) for(int j=0;j<=m;j++) if(j&(1<<21-i)) f[i][j]=f[i-1][j]; else f[i][j]=f[i-1][j]+f[i-1][j|(1<<21-i)]; for(int i=0;i<=m;i++) { int cnt=0,t=i; while(t) cnt+=t&1,t>>=1; if(cnt&1) ans=fk(ans-ksm(2,f[21][i])+1+mo); else ans=fk(ans+ksm(2,f[21][i])-1); } printf("%d\n",ans); return 0; }