其他分享
首页 > 其他分享> > [luogu p3214] [HNOI2011] 卡农

[luogu p3214] [HNOI2011] 卡农

作者:互联网

\(\mathtt{Link}\)

P3214 [HNOI2011] 卡农 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

\(\mathtt{Description}\)

在集合 \(S = {1, 2, \ldots, n}\) 中,选出 \(m\) 个子集,使得

求选定方案数。答案对 \(10^8+7\) 取模。

\(\mathtt{Data} \text{ } \mathtt{Range} \text{ } \mathtt{\&} \text{ } \mathtt{Restrictions}\)

\(\mathtt{Solution}\)

为什么不 % 1e9??为什么为什么为什么

本题最棘手的部分是第三个性质。

那么先不考虑第三个性质,只考虑前两个,容易发现一共可以有 \(2 ^n - 1\) 个非空集合可选,选 \(m\) 个。显然答案是 \(\dbinom{2^n - 1}{m}\)。

再考虑第三个性质,发现在这种情况下,如果选定了 \(m - 1\) 个集合已经确定,第 \(m\) 个集合实质上已经确定。因为在前 \(m - 1\) 个集合中出现了奇数次的元素一定要在这第 \(m\) 个集合中,出现了偶数次的元素一定不能在这第 \(m\) 个集合中,否则便不满足元素出现次数为偶数。

那么答案就是简单的 \(\dbinom{2^n - 1}{m - 1}\) 吗?很遗憾的发现,第三个性质成功维护,而第一个和第二个性质没维护上。也就是说:如果新的集合是空集,或者新的集合与前几个集合重复,这些情况还需要处理。

分别处理。

首先对于新的集合是空集的情况,其实就是不算新集合,前面 \(m - 1\) 个集合满足元素次数出现偶数次的情况。这提示我们递推:如果设 \(f_i\) 为 \(i\) 个集合中满足选法的方案数,事实上它是跟 \(f_{i - 1}\) 有关的,即新的集合是空集的情况有 \(f_{i - 1}\) 种。

顺着这个继续考虑重复。如果目前要选的第 \(m\) 个集合与前 \(m - 1\) 个集合中有一对重复的,考虑到去掉这两个集合后,剩下的 \(m -2\) 个集合正好有 \(f_{i - 2}\) 种方案。(因为原先是满足第三条性质的,去掉两个相同的集合,显然元素出现次数仍然都是偶数,这个性质仍然在维护)。而这两个集合则可以有 \(2^n - 1 - (i - 2)\) 种选法,所以新的集合与原来有所重复的情况一共有 \(f_{i - 2} \times (2^n - i + 1)\) 种。

注意到答案中求的是无序方案数,所以每一次递推中每种方案都重复了 \(i\) 次。我们需要再除以 \(i\).

总递推式:

\[f_i = \dfrac{\dbinom{2^n - 1}{i - 1} - f_{i - 1} - f_{i - 2} \times (2^n - i +1)}{i} \]

\(\mathtt{Time} \text{ } \mathtt{Complexity}\)

\(\operatorname{O}(n\log n)\)

\(\mathtt{Code}\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-04-22 23:37:20 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-04-23 00:22:39
 */
#include <iostream>
#include <cstdio>

#define int long long // 好习惯,此处 % 葵 %%%%%%%
//但是这样我真的很烦你知道吗?int所有高亮都变成了紫色,跟 #include 一样
//无语
inline int read() {
	int x = 0;
	bool flag = true;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-')
			flag = false;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + ch - '0';
		ch = getchar();
	}
	if (flag)
		return x;
	return ~(x - 1);
}

const int mod = 1e8 + 7;

inline int pow(int a, int b = mod - 2) {
	int s = 1;
	for (; b; b >>= 1, (a *= a) %= mod)
		if (b & 1)
			(s *= a) %= mod;
	return s;
}

signed main() {
	int n = read(), m = read();
	int S = pow(2, n) - 1;
	int C = S * (S - 1) % mod * pow(2) % mod;
	if (m <= 2) {
		puts("0");
		return 0;
	}
	int f = 0, g = 0, h = 0;
	for (int i = 3; i <= m; ++i) {
		f = (C - g - h * (S - i + 2 + mod) % mod + mod) % mod * pow(i) % mod;
		(C *= (S - i + 1 + mod) * pow(i) % mod) %= mod;
		h = g;
		g = f;
	}
	printf("%lld\n", f);
	return 0;
}

不错的题2!

标签:ch,int,luogu,偶数,HNOI2011,text,集合,mathtt,卡农
来源: https://www.cnblogs.com/crab-in-the-northeast/p/luogu-p3214.html