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;
}

 

posted @ 2019-10-20 14:58  LLTYYC  阅读(225)  评论(0编辑  收藏  举报