codeforces 449D Jzzhu and Numbers 容斥+DP

来源:互联网 发布:学做淘宝视频哪里看 编辑:程序博客网 时间:2024/06/06 15:36

很有意思的一题,题意是找到有多少个子序列,使得子序列每个元素的&值为0。

我们先考虑,如果每个都是0,那么方案显然是2^n-1,但是可能算多了。分别考虑第1位到第20位一定是1的情况,因为是&运算,选的一定是该位为1。然后统计两位的,三位的。。。答案就是 ∑ -1^d(x) * (  2^f(x )-1 )  ,x的范围的[0,1<<20),d(x)表示x改为二进制有多少个0,f(x)表示的是满足 ai&x == x的数字个数。

现在的问题关键是怎么计算f(x),因为直接暴力显然是不靠谱的,我们用dp[i][j] 表示某个状态,j是一个二进制数,我们不妨记j的低 i 位为x0,其他高位为x1,即 x1x0 == j ,x0中的1表示某个数的此位一定是1,0表示这个数的这一位可以是任意的数;而在x1中,1表示这个数的此位一定是1,0表示这个数的此位一定是0,满足这种情况的数字个数。显然dp[19][j] = f(j)。我们可以很容易得到转移方程(具体见代码)。

//#pragma comment(linker, "/STACK:102400000,102400000")#include<cstdio>#include<cstring>#include<vector>#include<queue>#include<cmath>#include<cctype>#include<string>#include<algorithm>#include<iostream>#include<ctime>#include<map>#include<set>using namespace std;#define MP(x,y) make_pair((x),(y))#define PB(x) push_back(x)typedef long long LL;//typedef unsigned __int64 ULL;/* ****************** */const LL INF=1LL<<60;const double INFF=1e100;const double eps=1e-8;const int mod=1000000007;const int NN=100005;const int MM=1000010;/* ****************** */int dp[20][1<<20];int a[1<<20];int q_pow(int x,int n,LL mod){    LL ans=1,xx=x;    for(;n>0;n>>=1)    {        if(n&1)ans=ans*xx%mod;        xx=xx*xx%mod;    }    return (int)ans;}int main(){    int n,i,j,mask=1<<20;    int ans,t,tt;    scanf("%d",&n);    for(i=1;i<=n;i++)        scanf("%d",&a[i]);    memset(dp,0,sizeof(dp));    for(i=1;i<=n;i++)    {        if(a[i]&1)        {            dp[0][ a[i] ]++;            dp[0][ a[i]^1 ]++;        }        else        {            dp[0][ a[i] ]++;        }    }    for(i=0;i<19;i++)        for(j=0;j<mask;j++)        {            t=1<<(i+1);            if(j&t)            {                dp[i+1][j]+=dp[i][j];                dp[i+1][ j-t ]+=dp[i][j];            }            else            {                dp[i+1][j]+=dp[i][j];            }        }    ans=0;    for(j=0;j<mask;j++)    {        t=1;        for(i=0;i<20;i++)            if((1<<i)&j)                t=-t;        tt=q_pow(2,dp[19][j],mod);        tt--;        if(tt<0)tt+=mod;        ans+=t*tt;        if(ans>=mod)ans-=mod;        if(ans<0)ans+=mod;    }    printf("%d\n",ans);    return 0;}


0 0
原创粉丝点击