BZOJ 2339: [HNOI2011]卡农 DP+容斥原理

题意:在 $n$ 个数的 $2^n-1$ 个非空子集中选取 $m$ 个,且必须满足:  

1. $m$ 个子集两两不同.

2. 每个数在 $m$ 个子集中出现总次数为偶数.   

3. 每个子集非空.       

令 $f[i]$ 表示选出了 $i$ 个集合且满足了上述 3 条限制的方案数.        

直接求 $f[i]$ 比较困难,考虑容斥:    

如果前 $i-1$ 个集合确定,那么集合 $i$ 也唯一确定 (即前 $i-1$ 个中出现奇数次的数).     

总:$A_{2^n-1}^{i-1}$.     

有 2 种情况不合法:1. 集合 $i$ 为空集 2. 与前面的一个集合相同.   

集合 $i$ 为空集,意味着前 $i-1$ 个集合中没有出现次数为奇数的数字,即 $f[i-1].$  

集合 $i$ 与一个集合 $j$ 相同,意味着除去集合 $i,j$ 之外的集合是合法的,即 $f[i-2].$     

而 $i$ 集合有 $2^n-1-(i-2)$ 种选取方式,所以这部分不合法的方案数是:$[2^n-1-(i-2)] \times f[i-2] \times (i-1)$.       

由于上述推导过程中默认集合是有序的,所以答案还要除一个 $m!$.

#include <bits/stdc++.h>  
#define N 1000007 
#define ll long long 
#define mod 100000007 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int qpow(int x,int y) 
{
    int tmp=1; 
    for(;y;y>>=1,x=(ll)x*x%mod) 
        if(y&1) tmp=(ll)tmp*x%mod; 
    return tmp; 
}
int A[N],f[N];  
int main()
{ 
    // setIO("input");  
    int i,j,n,m,inv; 
    scanf("%d%d",&n,&m);   
    inv=A[0]=1;   
    for(i=2;i<=m;++i) inv=(ll)i*inv%mod;   
    inv=qpow(inv,mod-2);   
    int mx=(qpow(2,n)-1+mod)%mod;     
    for(i=1;i<=m;++i) 
        A[i]=(ll)A[i-1]*(mx-i+1+mod)%mod;    
    f[0]=1,f[1]=0;   
    for(i=2;i<=m;++i) 
    {
        f[i]=(ll)(A[i-1]-f[i-1]+mod)%mod;  
        f[i]=(ll)(f[i]-(ll)f[i-2]*(i-1)%mod*(qpow(2,n)-1-(i-2)+mod)%mod+mod)%mod;  
    }
    printf("%d\n",(ll)f[m]*inv%mod);   
    return 0;
}

  

posted @ 2020-01-06 22:40  EM-LGH  阅读(168)  评论(0编辑  收藏  举报