BZOJ2839/LG10596 集合计数 题解(二项式反演+扩展欧拉定理)

题目大意:一个有 N 个元素的集合有 2N 个不同子集(包含空集),现在要在这 2N 个集合中取出若干集合(至少一个),使得它们的交集的元素个数为 K,求取法的方案数,答案模 109+7

为表述方便,不妨设这 i 个元素分别为 1n

前置知识:二项式反演。

考虑设 g(x) 为我们选择恰好 x 个数,要求选出若干个集合他们的交集里是这 x 个数的方案数,这个东西由于是“恰好”不好计数,考虑设 f(x) 表示说我们钦定 x 个数他一定要在这若干个集合的交集中的方案数。

f(x) 是好求的,我们直接从集合中钦定 x 个数一定要在若干个集合的交集中(其他数在不在我不关心),然后考虑包含这 x 个数的集合数量,显然除了这 x 个数必选以外,其余的数可选可不选,所以这部分的数量是 2nx 的。然后对于这些包含这 x 个数的集合,对于每个集合来说我们也是可选可不选,所以最终这部分的答案是 (nx)×(22nx1),这部分的 1 是因为要排除掉空集。

接下来考虑如果 我们已知 g,如何用 g 表示出 f(以方便我们用二项式反演从 f 反推回 g),显然f(x)=i=xng(i)×(ni),代表说我们对于每一个 ix,我们恰好选 i 个数的方案数之和就是钦定 x 个数的方案数。然后对这个式子用一下二项式反演:

f(x)=i=xn(ni)×g(i)g(x)=i=xn(1)i×(ni)×(ix)×(22ni1)

最后用一遍扩展欧拉定理就做完了,时间复杂度 O(nlogn),如果预处理 22i 可以做到 O(n)

const int MAXN = 1e6 + 5, mod = 1e9 + 7;
int n, k, fac[MAXN], ifac[MAXN];

int quickpow(int a, int b, int p = mod) {
	int ret = 1;
	while (b) {
		if (b & 1) ret = ret * a % p;
		a = a * a % p; b >>= 1;
	}
	return ret;
}

int C(int n, int m) {
	if (n < m) return 0;
	return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}

void work() {
	cin >> n >> k;
	fac[0] = 1;
	for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i % mod;
	ifac[n] = quickpow(fac[n], mod - 2);
	for (int i = n; i; i--) ifac[i - 1] = ifac[i] * i % mod; 
	int ans = 0, coff = 1;
	for (int i = k; i <= n; ++i) {
		ans = ans + coff * C(i, k) * C(n, i) % mod * quickpow(2, quickpow(2, n - i, mod - 1)) % mod;
		ans = (ans % mod + mod) % mod;
		coff = -coff;
	}
	cout << ans << endl;
}
posted @   小蛐蛐awa  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示