51nod 1407 与与与与 dp+容斥原理

来源:互联网 发布:js点击增加div 编辑:程序博客网 时间:2024/05/02 04:37

题意

有n个整数,问从他们中取出若干个数字相与之后结果是0的有多少组。
答案比较大,输出对于 1,000,000,007 (1e9+7)取模后的结果。
1<=n<=1,000,000,0<=a[i]<=1,000,000

分析

我们设f(x)表示有多少个i满足a[i]&x=x。
那么根据容斥原理,答案显然为220x=0(1)cnt(x)(2f(x)1)
现在考虑如何求出所有的f(x)。
fk(x)表示有多少个i满足对于前k个二进制位a[i]&x=x,对于剩余的二进制位而言,a[i]=x。
根据定有不难得到递推式:
若x的第k位为1则fk(x)=fk1(x)
反之则fk(x)=fk1(x)+fk1(k+2k)
那么就可以在O(nlogn)的时间内算出f(x)。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const int N=1100005;const int MOD=1000000007;int n,bin[25],f[25][N],pow[N],cnt[N];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*10+ch-'0';ch=getchar();}    return x*f;}int main(){    bin[0]=1;    for (int i=1;i<=20;i++) bin[i]=bin[i-1]*2;    n=read();    for (int i=1;i<=n;i++)    {        int x=read();        if (!(x&1)) f[0][x]++;        else f[0][x]++,f[0][x^1]++;    }    for (int i=1;i<20;i++)        for (int j=0;j<bin[20];j++)            if (j&bin[i]) f[i][j]=f[i-1][j];            else f[i][j]=f[i-1][j]+f[i-1][j^bin[i]];    for (int i=1;i<bin[20];i++) cnt[i]=cnt[i>>1]+(i&1);    pow[0]=1;    for (int i=1;i<=n;i++) pow[i]=pow[i-1]*2,pow[i]-=pow[i]>=MOD?MOD:0;    LL ans=0;    for (int i=0;i<bin[20];i++)        if (cnt[i]&1) (ans-=pow[f[19][i]]-1)%=MOD;        else (ans+=pow[f[19][i]]-1)%=MOD;    ans+=ans<0?MOD:0;    printf("%lld",ans);    return 0;}