DistinctAdjacent
[ABC307E] Distinct Adjacent
解答1:
我们将圆环上有 \(i\) 个人的情况的答案记为 \(f(i)\)。考虑有 \(i+1\) 个人排成一排的情况,此时答案为 \(M \times (M-1)^i\)。这样的传递方式中,如果是圆环的情况且不满足条件的传递方式只有人 \(i+1\) 和人 \(1\) 的情况不同。因此,这两个人可以视为同一人,这样的传递方式有 \(f(i)\) 种。因此,我们有 \(f(i+1)=M \times (M-1)^i-f(i)\)。这样就解决了这个问题。
代码
解答2:
DP
这道题可以看作是 ABC256G 和 ABC232E 的简化版。
圆环上的DP
对于一排排列的情况,我们可以考虑 DP,定义状态 \(\mathrm{DP}[i][x]\) 表示前 \(i\) 个人已经分配完毕,第 \(i\) 个人手中的数字为 \(x\),并且前 \(i\) 个人中没有相邻的人手中的数字相同的方案数。这样的话,我们只需要考虑第 \(i\) 个人手中的数字和第 \(i-1\) 个人手中的数字是否相同即可。最后的答案就是 \(\sum_{x=0}^{M-1} \mathrm{DP}[N][x]\)。
对于围成一圈的情况,我们可以先固定第一个人手中的数字为 \(k\),然后考虑 DP,定义状态 \(\mathrm{DP}_k[i][x]\) 表示前 \(i\) 个人已经分配完毕,第 \(1\) 个人手中的数字为 \(k\),第 \(i\) 个人手中的数字为 \(x\),并且前 \(i\) 个人中没有相邻的人手中的数字相同的方案数。这样的话,我们只需要考虑第 \(i\) 个人手中的数字和第 \(i-1\) 个人手中的数字是否相同即可。最后的答案就是 \(\sum_{k=0}^{M-1} \sum_{x \neq k} \mathrm{DP}_k[N][x]\)。
但是,这样的 DP 状态数和转移数都是 \(O(NM^N)\) 的,无法通过本题。因此,我们需要进行状态的优化。注意到,我们只需要知道第 \(i\) 个人手中的数字和第 \(1\) 个人手中的数字是否相同即可,因此我们可以将状态 \(\mathrm{DP}[i][x]\) 中的 \(i\) 去掉,只保留一个布尔值 \(f\) 表示第 \(i\) 个人手中的数字和第 \(1\) 个人手中的数字是否相同。这样的话,状态数就变成了 \(O(N)\),转移数也是 \(O(1)\) 的。最后的答案就是 \(\mathrm{DP}[N][\mathrm{false}]\)。
例题:
另外,我们还可以使用包除原理来求解。注意到,我们需要满足 \(N\) 个条件,即相邻的两个人手中的数字不能相同。我们可以考虑使用包除原理,设 \(S_k\) 表示恰好有 \(k\) 个相邻的人手中的数字相同的方案数,则最终的答案为 \(\sum_{k=0}^N (-1)^k \binom{N}{k} M^{\max(1, N-k)}\)。这个式子的计算量是 \(O(N)\) 的,可以通过本题。
解答3:
答案是 \((-1)^N(M-1)+(M-1)^N\)。可以通过以下几种方法得出:
- 包括排斥原理
使用包括排斥原理,可以得出方案数为
整理得到
- DP 的优化
公式解释中的 DP 可以写成矩阵乘法的形式,因为转移不依赖于 \(i\)。具体来说,答案可以表示为
整理得到 \(a_n=(-1)^n(M-1)+(M-1)^n\)。也可以通过解 \(2\) 阶线性递推式得到这个结果,即
以下是一个实现的例子:
#include <bits/extc++.h>
#include <atcoder/modint>
int main() {
using namespace std;
int N, M;
cin >> N >> M;
atcoder::static_modint<998244353> m = M - 1;
cout << (m.pow(N) + (N & 1 ? -m : m)).val() << endl;
return 0;
}
N, M = map(int, input().split())
p = 998244353
print((pow(M - 1, N, p) + pow(-1, N, p) * (M - 1)) % p)