bzoj2839
容斥原理+组合数学
看见这种恰有k个的题一般都是容斥原理,因为恰有的限制比较强,一般需要复杂度较高的方法枚举,而容斥就是转化为至少有k个,然后通过容斥原理解决
我们先选出k个元素作为交集,有C(n,k)种可能,那么剩下的n-k个元素既可以选也可以不选,一共有2^(n-k)种选法,每种选法对应了一个集合,也就是说一共有2^(n-k)种不同的集合,我们希望在这n-k个元素中选出若干个集合,使他们的交集为空,于是我们枚举选多少个元素,i=0->n-k,这样有C(n-k,i)种选法,然后我们使用容斥原理来计算i个元素交集为空集的集合数量,对于给定元素交集大小至少为i的情况,我们可以跟刚才一样先选出i个元素作为交集,方案数同上,然后方案数是2^(2^(n-i-k))-1,因为我们有2^(n-i-k)个集合,每个集合可以选或不选,因为已经选出i个元素作为交集,所以交集大小至少是i,其他的集合随便选就满足至少是i
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<set> #include<map> using namespace std; typedef long long ll; const int N = 1000010, mod = 1000000007; int n, k; ll ans, pw = 2; ll inv[N], fac[N], facinv[N]; ll C(int n, int k) { return fac[n] * facinv[k] % mod * facinv[n - k] % mod; } int main() { scanf("%d%d", &n, &k); inv[0] = inv[1] = fac[0] = fac[1] = facinv[1] = facinv[0] = 1; for(int i = 2; i <= n; ++i) { fac[i] = fac[i - 1] * (ll)i % mod; inv[i] = (mod - mod / i) * inv[mod % i] % mod; facinv[i] = facinv[i - 1] * inv[i] % mod; } for(int i = n - k; i >= 0; --i) { ans = (((ans + ((i & 1) ? -1 : 1) * C(n - k, i) * ((pw - 1) % mod + mod) % mod) % mod) % mod + mod) % mod; pw = pw * pw % mod; } ans = ((ans * C(n, k) % mod) % mod + mod) % mod; printf("%lld\n", ans); return 0; }