51nod 1407 与与与与

来源:互联网 发布:网络剧余罪 编辑:程序博客网 时间:2024/04/19 01:41
1407 与与与与
题目来源: CodeForces
基准时间限制:1.5 秒 空间限制:131072 KB 分值: 320 难度:7级算法题
有n个整数,问从他们中取出若干个数字相与之后结果是0的有多少组。
答案比较大,输出对于 1,000,000,007 (1e9+7)取模后的结果。
Input
第一行输入一个整数n。(1<=n<=1,000,000).
第二行有n个整数a[0],a[1],a[2],...a[n-1],以空格分开.(0<=a[i]<=1,000,000)
Output
对于每一组数据,输出一个整数。
Input示例
3
2 3 3
4
0 1 2 3
Output示例
0

10

题解:对于我这中蒟蒻来说就是神题,看了一天才真正看明白。对于几个数&等于0,有非常多种情况而且状态不好表示,所以就用总的组合方案数(2^n-1)减去相&!=0的方案数即合法方案数。对于几个数&!=0,那么转化成二进制数来看,也就是说n个数最大能合成2^20-1,2^20约等于1000000。那么就枚举0到2^20-1的所有数,其中必定包含所有的ai和所有ai组合合成的数。对于每一个ai,如果aj&ai!=0,那么ai和aj在二进制状态下一定有一位同时为一,那么需要找出ai的所有位中为1的位,在其他a中那些为也为1的和ai组合一定为1,设f【i】表示和i这个数相&任然等于i的a的方案数,由&的运算法则:同为1 才为一,所以得出f【i】所包含的所有a相&一定!=0,(因为在i的每一个“1”位,那些a一定是1)。所以这f【i】个数组合的所有方案数均不合法。最终统计答案用总方案数-不和法方案数=合法方案数,所以这里又要用到容斥原理,

ans=总方案数(2^n-1)-相&后的二进制中有一个1+相&后二进制中有两个1-相&后二进制中有三个1……=相&后没有1(即为0)

总结:容斥原理的题感觉都很#人,所以要多做题,善于发现各个方案数重复的那部分。

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#define mod 1000000007#define N 3000000using namespace std;inline int read(){    int ra,fh;char rx;    rx=getchar(),ra=0,fh=1;    while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();    if(rx=='-')fh=-1,rx=getchar();    while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra*fh;}int n,x,dp[N],g[N];long long f[N];long long ans=0;int main(){scanf("%d",&n);int mx=(1<<20)-1;for(int i=1;i<=n;i++){x=read();dp[x]++;}f[0]=1;for(int i=1;i<=n;i++) f[i]=(f[i-1]<<1)%mod;for(int i=20;i>=1;i--)    for(int j=mx;j>=0;j--)        if(!(j & (1<<(i-1))))             dp[j]+=dp[j|(1<<(i-1))];ans=f[n]-1;for(int i=1;i <= mx;i++){g[i]=g[i>>1]+(1&i);if(g[i]&1) ans=(ans-f[dp[i]]+1+mod)%mod;else ans=(ans+f[dp[i]]-1+mod)%mod;}printf("%lld\n",ans);return 0;}


原创粉丝点击