P3214 [HNOI2011] 卡农
很好的计数题呢,代码很短很 atcoder 的题。
思路
考虑 dp。
设 \(dp_i\) 表示有 \(i\) 个片段的合法种数。这是不好直接算的。我们注意到假设我们随意选了 \(i - 1\) 个不重区间,为了让每个音阶都被演奏偶数次,我们第 \(i\) 个集合是可以确定的,这样子的方案数应为:
\[\binom{2 ^ n - 1}{i - 1}
\]
\(2 ^ n - 1\) 表示 \(n\) 个音节构成的非空集合数。于其中选 \(i\) 个即为答案。
显然这个并不是 \(dp_i\) 真正的值。
首先,有可能第 \(i\) 个集合(即那个通过前 \(i - 1\) 个集合确定的集合)可能为空集,这样子的方案数字显然是 \(dp_{i - 1}\)。
其次,有可能第 \(i\) 个集合可能与前 \(i - 1\) 个集合有重复,这样的贡献为 \(dp_{i - 2} \times (2 ^ n - 1 - (i - 2))\),其中 \(2 ^ n - 1 - (i - 2)\) 为第 \(i\) 个集合可能的方案数。
最后,这样子我们考虑到答案应为无序的,那么每种方案数这样是被算了 \(i\) 次的。
综上,
\[dp_i = (\binom{2 ^ n - 1}{i - 1} - dp_{i - 1} - dp_{i - 2} \times (2 ^ n - 1 - (i - 2))) \times \frac{1}{i}
\]
做完了。
代码
注意到无法预处理 \(2 ^ n - 1\) 的阶乘,所以说组合数需要单独递推求。
void ACehomoxue() {
cin >> n >> m;
dp[0] = 1, dp[1] = 0;
binomm = 1;
for(int i = 2; i <= m; i++) {
binomm = binomm * Mod(ksm(2, n) - i + 1) % mod * ksm(Mod(i - 1)) % mod;
dp[i] = Mod(binomm - dp[i - 1] - dp[i - 2] * (ksm(2, n) - 1 - (i - 2)) % mod) * ksm(i) % mod;
}
cout << dp[m], el;
}

浙公网安备 33010602011771号