CodeForces 449 D.Jzzhu and Numbers(状压DP+容斥原理)

来源:互联网 发布:php 倒计时功能 编辑:程序博客网 时间:2024/06/13 05:59

Description
给出一个n个数a[i]的集合S,问S有多少个子集&起来是0
Input
第一行一整数n表示集合元素数量,之后n个整数a[i]表示该集合中的元素(1<=n<=1e6,0<=a[i]<=1e6)
Output
输出合法子集数量,结果模1e9+7
Sample Input
4
0 1 2 3
Sample Output
10
Solution
设dp[i]为n个数中&上i后结果还是i的数的个数,做一遍高维前缀和(或者说数位DP)即可得到dp数组,进而&和至少为i的子集数量为2^dp[i],然后由容斥原理即可得到&和是0的子集数量,时间复杂度O(nlogn)
Code

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<vector>#include<queue>#include<map>#include<set>#include<ctime>using namespace std;typedef long long ll;#define INF 0x3f3f3f3f#define maxn 1000001const ll mod=1e9+7;ll dp[maxn],f[maxn];void init(){    f[0]=0;    for(int i=1;i<maxn;i++)f[i]=(f[i-1]*2ll+1)%mod;}int main(){    init();    int n,a;    while(~scanf("%d",&n))    {        memset(dp,0,sizeof(dp));        for(int i=1;i<=n;i++)        {             scanf("%d",&a);            dp[a]++;        }        for(int i=0;i<20;i++)            for(int j=0;j<maxn;j++)                if(j&(1<<i))                    dp[j^(1<<i)]+=dp[j];        ll ans=0;        for(int i=0;i<maxn;i++)        {            int num=0;            for(int j=0;j<20;j++)                if(i&(1<<j))num++;            if(num&1)ans=(ans-f[dp[i]]+mod)%mod;            else ans=(ans+f[dp[i]])%mod;        }        printf("%I64d\n",ans);    }    return 0;}
0 0
原创粉丝点击