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; }