简要题意
一个有 \(N\) 个元素的集合有 \(2N\) 个不同子集(包含空集),现在要在这 \(2N\) 个集合中取出若干集合(至少一个),使得它们的交集的元素个数为 \(K\),求取法的方案数,答案模 \(10^9+7\)。
数据范围:\(1\le K\le N\le10^6\)。
题解
我们设 \(f(i)\) 表示选出子集大小恰好为 \(i\) 的方案数,然而我们发现这个东西不好转移。但是,如果我们先求出一个限制条件少一点但是比较好求的东西 \(g(i)\) 再去求 \(f(i)\) 就会简单许多(也许。
于是就有 \(g(i)\) 表示钦定 \(i\) 个元素在交集中(其他元素不考虑),这样 \(g(i)\) 就比较好求了,我们可以把 \(g(i)\) 的式子写出来:\(g(i)={n\choose i}(2^{2^{n-i}}-1)\)。为什么呢?首先我们需要从 \(n\) 个元素中选择 \(i\) 个元素,所以有 \(n\choose i\),然后对于剩下 \(n-i\) 个元素,我们可以列举出可能存在的集合的可能,也就是 \(2^{n-i}\) 种可能,对于这些集合我们可以选可以不选,但是题目要求至少选一个,所以就是一个 \(2^{n-i}\) 元集去掉空集,然后选的元素与不选的元素之间互有影响所以是乘法原理。
然后就是去找 \(f\) 与 \(g\) 的关系了。其实对于 \(g\),我们还有另一种求法:\(g(i)=\sum^n_{j=i}{j\choose i}f(j)\)。其实就是对于选出子集大小恰为 \(j\) 的方案中再去选出 \(i\) 个,与第一种方法等价。
然后看到后面这坨直接二项式反演就可以得到:
\[f(i)=\sum^n_{j=i}(-1)^{j-i}{j\choose i}{n\choose j}(2^{2^{n-j}}-1) \]然后直接算就行。
代码
signed main(){
n = rd(), k = rd();
fac[0] = bs[0] = 1;
for(int i = 1; i <= n; ++i)fac[i] = fac[i - 1] * i % mod, bs[i] = bs[i - 1] * 2 % (mod - 1);
inv[n] = qmi(fac[n], mod - 2);
for(int i = n - 1; i; --i)inv[i] = inv[i + 1] * (i + 1) % mod;
for(int i = k; i <= n; ++i){
ll op = qmi(- 1, i - k);
ll t1 = C(i, k), t2 = C(n, i);
ll tt = (qmi(2, bs[n - i]) - 1 + mod) % mod;
(ans += op * t1 % mod * t2 % mod * tt % mod + mod) %= mod;
}
cout << ans;
return 0;
}
标签:P10596,题解,元素,子集,choose,luogu,集合,我们
From: https://www.cnblogs.com/Nekopedia/p/18420664