zoukankan      html  css  js  c++  java
  • Jzoj3170 挑选玩具

    ABC找到N个箱子,箱子里装着一些玩具,一共有M种玩具,编号从1到M,同一种玩具可能出现在多个箱子里。

    ABC决定从中选择一些箱子,把这些箱子中的玩具聚集到一起,必须保证每种玩具至少出现一次。

    问ABC一共有多少种选择方案 (1<=N<=1,000,000,1<=M<=20)

    第三次看到这个题了,前两次都忘了怎么做了。。。

    好的第一眼看上去可以dp做,结果发现只能过50%数据

    矩阵明显不可做,让后开始考虑数学方法

    考虑容斥原理,我们发现可以用总方案减去至少不出现一种玩具的方案数

    令f(S)表示选出的方案中,所有元素都在S中的方案数(但是不要求S每个元素都要出现)

    令g(S)表示选出的方案中,不包含任何一个S含有的元素的方案数

    那么可以得到Answer=2^N-Σg(S)*(-1)^|S|,g(S)=f(~S)

    现在考虑如何快速计算f

    我们发现如果对于每个S都求出有几个给出的集合x满足x∩S=x就可以快速计算得到f(S)=2^x-1

    而我们发现,f(S)一定被包含于f(S∪M)

    所以可以运用分治法来解决:f[x+m]+=f[x]

    最后直接统计即可

    #pragma GCC opitmize("O3")
    #pragma G++ opitmize("O3")
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #define N (1<<m)
    #define M 1000000007
    using namespace std;
    int c[1<<20],w[1<<20],f[1<<20],p[1<<20]={1},n,m,S=0;
    void cdq(int l,int r){
    	if(l==r){
    		f[l]=c[l];
    		return ;
    	}
    	int m=l+r>>1;
    	cdq(l,m); cdq(m+1,r);
    	for(int i=l;i<=m;++i) f[i+m-l+1]+=f[i];
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int x,y,z,i=1;i<=n;++i){
    		for(z=0,scanf("%d",&x);x--;z+=1<<y-1)
    			scanf("%d",&y);
    		++c[z];
    	}
    	for(int i=1;i<N;++i) w[i]=w[i>>1]^(i&1);
    	for(int i=1;i<=n;++i) p[i]=(p[i-1]<<1)%M;
    	cdq(0,N-1); 
    	for(int i=0;i<N;++i)
    		S=w[i]?(S-p[f[N-i-1]]+1)%M:(S+p[f[N-i-1]]-1)%M;
    	printf("%d",(S+M)%M);
    }

  • 相关阅读:
    转 Python常见数据结构整理
    转 Python爬虫实战二之爬取百度贴吧帖子
    转 Python——UnicodeEncodeError: 'ascii' codec can't encode/decode characters
    慕课 python 操作数据库2 银行转账实例
    转 Python爬虫实战一之爬取糗事百科段子
    SQL 锁 lock
    SQL SERVER CROSS/OUTER APPLY 用法
    sql server 怎样用select语句调用自定义表值函数
    计算日期类型
    行转列:SQL SERVER PIVOT与用法解释
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/8312584.html
Copyright © 2011-2022 走看看