给出一个长度为n的序列 。求构造出一个序列 使得 。求方案数模 。
也就是从 里面选出一个非空子集使这些数按位与起来为0。
首先记 “包含” 当且仅当 ,那么可以用 表示按位与和包含 的最长子序列长度,有转移 ,其中 包含 。边界是 。
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[a[i]]++;
}
for(int j=0;j<=25;j++) // 注意转移顺序,要先枚举 j 再枚举 i,要不然会算重
{
for(int i=1;i<=1000002;i++)
{
if((i>>j)&1)
{
dp[i^(1<<j)]=dp[i^(1<<j)]+dp[i];
}
}
}
考虑答案的求解,令 表示与和包含 的子序列个数,显然有 。试着用容斥求出答案,令 表示所有满足 的二进制中有 个 的 的和,答案即为
完整代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int mod=1000000007;
int n,a[1000005];
int bse[1000005],dp[1000005],g[30];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[a[i]]++;
}
for(int j=0;j<=25;j++) // 注意转移顺序,要先枚举 j 再枚举 i,要不然会算重
{
for(int i=1;i<=1000002;i++)
{
if((i>>j)&1)
{
dp[i^(1<<j)]=dp[i^(1<<j)]+dp[i];
}
}
}
bse[0]=1;
for(int i=1;i<=n;i++)
{
bse[i]=2ll*bse[i-1]%mod;
}
for(int i=0;i<=1000002;i++)
{
int cnt=0;
for(int j=0;j<=25;j++)
{
cnt+=(i>>j)&1;
}
g[cnt]=((long long)g[cnt]+bse[dp[i]]-1+mod)%mod; // 长度为 dp[i] 的序列里取子序列有 2^dp[i]-1 种方案
}
int ans=g[0];
for(int i=1;i<=25;i++)
{
if(i&1)
{
ans=(ans-g[i]+mod)%mod;
}
else
{
ans=(ans+g[i])%mod;
}
}
printf("%d\n",ans);
return 0;
}