题意
给定一个n元集,元素为
分析
要是n和a的范围小一些自然可以直接用01背包做,然而这里a与n都达到了1e6的范围,则要另寻他法。
先求有多少种情况使得与出来的结果不为0,考察有多少元素在某些二进制位上为1,则它们与出来在这些位上也必然为1。 令函数
那么有了f(x)以后,就可以用容斥原理求出有多少种使得结果不为1的情况。再令
注意2的幂要减一,因为不能一个也不选。x的上限取到2e20,刚好比1e6大,能满足所有位。
这时再求为0的情况:
现在问题在于如何快速求得f(x)。先将每个a和x分为前k位和后20-k位两部分,
设状态:
转移方程:
显然dp[x][20]=f(x)
AC代码
//CodeForces 449D Jzzhu and Numbers
//AC 2017-1-19 14:21:28
//DP, Inclusion-exclusion
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=1e6+100;
int n;
int a[maxn];
int dp[(1<<21)][21];
long long powmod(long long x,long long y,long long m)
{
long long z=x%m;
if(!y)
return 1;
if(y&1)
return z*powmod(z*z,(y-1)/2,m)%m;
else
return powmod(z*z,y/2,m)%m;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d",a+i);
for(int i=0;i<n;++i)
++dp[a[i]][0];
for(int i=1;i<21;++i)
{
for(int j=0;j<(1<<21);++j)
{
if((j>>(i-1))&1)
dp[j][i]=dp[j][i-1];
else
dp[j][i]=dp[j][i-1]+dp[j+(1<<(i-1))][i-1];
}
}
long long ans=0;
for(int i=0;i<(1<<21);++i)
{
long long cur=powmod(2,dp[i][20],mod)-1;
if(__builtin_popcount(i)&1)
cur=-cur;
ans+=cur+mod;
ans%=mod;
}
cout<<ans<<endl;
return 0;
}