[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 ;
}
posted @ 2019-01-17 17:43  beretty  阅读(188)  评论(0编辑  收藏  举报