洛谷 P3214 [HNOI2011] 卡农

题目传送门


前言

再次败在 \(dp\) 手下,但是数据范围这么小应该是可以看出是 \(dp\) 的(毕竟对于其他组合数的问题数据范围都是 \(10^9\) 起步)。


思路

题意简化

现有 \(1, 2, 3, ... , n - 1, n\),从其所组成的 \(2^n - 1\) 个非空集合中选出 \(m\) 个,每个集合只能选一次,求【使 \(\forall i \in [1, n]\) 在所选集合中,出现次数均为偶数的】的选择方案书,答案对 \(10^8 + 7\) 取模。

明确两点:

  1. 出现 \(0\) 次也算出现偶数次;
  2. 一个集合中,不可能出现两个相同的数(集合的互斥性)。

状态设计

\(dp_i\) 表示选了 \(i\) 个集合的合法方案数。
注意:在之后的转移使,我们先 只需要让这 \(i\) 个集合 满足“每个数出现次数为偶数”的限制。

状态转移

限制 \(1\):每个数只能出现偶数次

首先看起来题目所给的“每个数出现偶数次”的限制不太好弄,所以我们先考虑假如已经有 \(i - 1\) 个确定的集合,可以怎么添加一个集合,使得这 \(i\) 个集合满足这条限制。

对于一个数 \(x\)

  1. 假如它在前 \(i - 1\) 个集合中出现了奇数次,那么它一定要在最后一个集合在出现一次,这样才能保证 \(x\)\(i\) 个集合中总共出现了偶数次;
  2. 假如它在前 \(i - 1\) 个集合中出现了偶数次,那么它就不可能出现在最后一个集合中,因为为了维持其出现偶数次,且每个集合不能出现相同的数,所以在最后一个集合中,其出现次数只能为 \(0\)

由上述看题解分析,假如已经确定了 \(i - 1\) 个集合,那最后一个集合就是确定的。

先不管其他限制,就但从这条限制来看,它就有 \(A_{2^n - 1}^{i - 1}\) 种可能(这里之所以是排列不是组合,是因为题目让我们求出的【所选集合方案是不顾选出集合顺序的】,所以我们在最后让答案乘以 \(m!\) 的逆元就好了)。

限制 \(2\):集合不能为空集

由于可能在前 \(i - 1\) 个集合中,每一个数都出现了偶数次,所以在最后一个集合中任何数都不能出现,即最后一个集合使空集,这样是不合法的。

我们从容斥角度考虑转移,看看最后一个集合为空集的有多少种可能。

把最后一个空集去除后,发现剩下的 \(i - 1\) 个集合正好是一个满足【取 \(i - 1\) 个集合使其满足 “每个数只能出现偶数次” 限制】的取集合方案(因为若最后一个集合为空集,那么前 \(i - 1\) 个集合就必定满足 “每个数出现偶数次” 限制),这样的情况有 \(dp_{i - 1}\) 种。

所以在总的选择方案中减去 \(dp_{i - 1}\) 就行。

限制 \(3\):每个集合只能选一次,即不能出现两个相同的集合

由于前 \(i - 1\) 个集合一定互不相同(因为我们是从 \(n\) 个数组成的 \(2^n - 1\) 个互不相同的集合中选出的),所以只考虑【第 \(i\) 个集合与前 \(i - 1\) 个集合中】有一个相同的方案。

先明确一点:假如 \(i\) 与前 \(i - 1\) 个集合中的某个相同(假设是第 \(j\) 个集合),那么决定第 \(i\) 个集合构造方法的就是集合 \(j\)

因此,我们如果去除集合 \(i, j\),那么剩下的 \(i - 2\) 个集合一定能形成一组满足【取 \(i - 2\) 个集合使其满足 “每个数只能出现偶数次” 限制】的取集合方案。因为两个集合中的数相同,因此删除这两个集合的话,集合中的数也是成对被删除的,因此能构成。这样的 \(i - 2\) 个集合共有 \(dp_{i - 2}\) 种。

又因为 \(j\)\(i - 1\) 种取法,集合 \(i\)\(2^n - 1 - (i - 2)\) 种(即在所有的 \(2^n - 1\) 个集合中,去除【已有的 \(i - 2\) 个集合】剩下的之中,再选一个),剩下的 \(i - 2\) 个集合共有 \(dp_{i - 2}\) 种,所以这个非法方案数是 \((i - 1) \times (2^n - 1 - (i - 2)) \times dp_{i - 2}\)

综上,转移方程就是:

\[dp_i = A_{2^n - 1}^{i - 1} - dp_{i - 1} - (i - 1) \times (2^n - 1 - (i - 2)) \times dp_{i - 2} \]

边界条件

首先是 \(dp_1 = 0\):因为只选一个非空的不重集合就让每个数出现偶数次显然是不可能的;
其次是 \(dp_2 = 0\):因为要在前两个集合中,就使每个数出现偶数次,要么得是两个空集,要么两个集合就得一样,这样显然也是不合法的。

答案

\(dp_m \times inv(m!)\),因为题目要求出的集合是不顾 \(m\) 个集合顺序的,而在计算时我们有考虑了其顺序,所以要除去 \(m!\)

复杂度

时间空间均为\(O(m)\)


代码

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int maxn = 1e6 + 7;
const int mod  = 1e8 + 7;

int n, m;
int facm = 1, invm;
int tot;
int qpow(int x, int y) {
	int res = 1;
	for (; y; y >>= 1, x = x * x % mod)
		if (y & 1) res = res * x % mod;
	return res;
}

int dp[maxn], A[maxn];
signed main() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= m; ++i)
		facm = facm * i % mod;
	invm = qpow(facm, mod - 2);
	tot = (qpow(2, n) - 1 + mod) % mod;
	A[0] = 1;
	for (int i = 1; i <= m; ++i)
		A[i] = A[i - 1] * (tot - i + 1) % mod;
	dp[1] = dp[2] = 0;
	for (int i = 3; i <= m; ++i)
		dp[i] = ((A[i - 1] - dp[i - 1] - 
				 (tot - i + 2) * (i - 1) % mod * dp[i - 2] % mod) % mod + mod) % mod;
	printf("%lld\n", dp[m] * invm % mod);
	return 0;
}
posted @ 2025-04-05 15:49  syzyc  阅读(27)  评论(0)    收藏  举报