[BZOJ2839:]集合计数
Description
一个有N个元素的集合有2N个不同子集(包含空集),现在要在这2N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)
Input
一行两个整数N,K
Output
一行为答案。
Sample Input
3 2
Sample Output
6
HINT
【样例说明】
假设原集合为{A,B,C}
则满足条件的方案为:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}
数据说明:对于100%的数据,1≤N≤1000000;0≤K≤N;
题解
我竟然还能写出容斥题来
元素的交集恰好是k不好计算
那么我们就去考虑容斥
设\(f[i]\)表示有至少i个相交元素的方案数
那么\(f[i] = (-1)^{i-k}*C(n , i)*(2^{2^{n - i}}-1)\)
然后发现这样不对
因为每种有i个相交元素的方案对\(f[k]\)的贡献其实是\(C(i,k)\)
所以我们要把容斥系数修改成\((-1)^{i-k} * C(i , k)\)
让\(f[i] = (-1)^{i-k}*C(i,k)*C(n , i)*(2^{2^{n - i}}-1)\)
然后注意指数不能对\(1e9+7\)取膜!这玩意儿坑死我了
upd : 也可以用二项式反演做不过是一样的
设\(g(i)\)表示至少有\(i\)个相交元素的方案数
\(f(i)\)表示恰好有\(i\)个元素的方案数
那么显然\(g(i)\)很好求,就是\(g(i)=\sum_{j=i}^{n}{C(n,j)*(2^{2^{n-j}}-1)}\)
然后\(g(i)=\sum_{j=i}^{n}{C(j,i)*f(j)}\)
所以我们可以对\(g(i)\)反演得\(f(i)=\sum_{j=i}^{n}{(-1)^{j-i}*C(j,i)*g(j)}\)
代码
#include<cstdio>
#include<algorithm>
# define int long long
const int M = 1000005 ;
const int mod = 1e9 + 7 ;
using namespace std ;
int n , k , Ans , fac[M] ;
inline int Fpw(int Base , int k) {
int temp = 1 ;
while(k) {
if(k & 1) temp = (temp * Base) % mod ;
Base = (Base * Base) % mod ; k >>= 1 ;
}
return temp ;
}
inline int inv(int x) { return Fpw(x , mod - 2) ; }
inline int C(int n , int m) { return (fac[n] * inv((fac[m] * fac[n - m]) % mod)) % mod ; }
# undef int
int main() {
# define int long long
scanf("%lld%lld",&n,&k) ;
fac[0] = 1 ; for(int i = 1 ; i <= n ; i ++) fac[i] = (fac[i - 1] * i) % mod ;
int pw = 1 , ret = (n - k) % 2 ? -1 : 1 ;
for(int i = n ; i >= k ; i --) {
Ans = (Ans + ret * C(i , k) * C(n , i) % mod * pw % mod + mod) % mod ;
pw = (((pw + 1) * (pw + 1) - 1) % mod + mod) % mod ; ret *= -1 ;
}
printf("%lld\n",Ans) ;
return 0 ;
}