• 一道容斥神仙题


    题意:

    简略题意: 

    n <= 1e6,0 <= A[i] <= 1e6

    首先有一个很显然的n * A[i]的子集DP,这里就不再赘述

    这题的难点在于把数字看成集合,然后求若干个集合并起来为空的方案数,并起来为空很不想,我们可以考虑并起来恰好有0个元素,"恰好"是不是有二次项反演那感觉了?可惜这题限制条件太多,不好反演或者反演不了

    但这给我们启发,毕竟一切二次项反演题基本都可以用容斥来做,我们可以考虑用容斥来做,加上并起来恰好为0个元素的,减去恰好为1个元素的......所以我们现在要求有多少个集合并起来至少有i个元素,转化为数字即(bit(x) == i)

    (bit[x]即x的二进制下有几个一),我们可以先钦定有x个元素,即枚举x的超集,设为dp[x],从数字上也可以理解为(满足A[i] & x == A[i])的i有多少个,这个显然可以枚举子集暴力DP,然后每个x的超集可以可选可不选,但不能全不选(全不选并完就变成了空集)

    所以对于单个数x贡献是2^dp[x] - 1;然后容斥一下即可(注意在集合和数字间转化,个人觉得是本题最大难点)

    代码如下:

    /*阴影之内*/
    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    using namespace std;
    #define ll long long
    int read(){
    	char c = getchar();
    	int x = 0;
    	while(c < '0' || c > '9')	c = getchar() ;
    	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar() ;
    	return x;
    } 
    const int maxn = 1e6 + 10;
    const int mod = 1e9 + 7;
    int dp[maxn];
    int mi[maxn];
    void init(){
    	mi[0] = 1;
    	for(int i = 1; i <= maxn - 10; ++i)		mi[i] = 2ll * mi[i-1] % mod;
    }
    int main(){
    	int n = read();
    	init();
    	for(int i = 1; i <= n; ++i){
    		int x = read();dp[x]++;
    	}
    	for(int i = 0; i <= 20; ++i)
    		for(int j = 0; j <= maxn - 10; ++j)
    			if(j & (1 << i))
    				dp[j^(1<<i)] += dp[j];
    	ll Ans = 0;
    	for(int i = 0; i <= maxn - 10; ++i){
    		int num = 0;
    		for(int j = 0; j <= 20; ++j)
    			if(i & (1 << j))	num++;
    		if(num & 1) 	Ans = (Ans - (mi[dp[i]] - 1) + mod) % mod;
    		else	Ans = (Ans + (mi[dp[i]] - 1)) % mod;
    	}
    	cout<<Ans<<endl;
    	return 0;
    }
    

      

  • 相关阅读:
    SpringKafka——消息监听
    RabbitMQ(八)线程池消费
    RabbitMQ(七)延迟队列
    window计划任务 0x1
    获取网页URL地址及参数等的两种方法(js和C#)
    HttpWebRequest的偶尔请求超时问题
    用HTML、CSS、JS制作圆形进度条(无动画效果)
    批量关联update
    仅仅 IE8 有效的 CSS hack 写法
    SqlServer关闭与启用标识(自增长)列
  • 原文地址:https://www.cnblogs.com/y-dove/p/13455478.html
Copyright © 2020-2023  润新知