AtCoder ABC217F Make Pair
ABC217F - Make Pair
tag: 区间 DP,计数
有 \(2N\) 名学生排成一列,从左到右编号分别为 \(1,2,\cdots,2N\),其中已知 \(M\) 对学生 \(A_i,B_i\)(\(1\le i\le M\))为好友。
老师将进行以下操作 \(N\) 次,最终形成 \(N\) 对学生组合:
- 每次选择 相邻且互为好友 的两名学生,将他们配对并从队列中移除;
- 若被移除的学生不在队列两端,则左右两侧的学生会靠拢成为新的相邻关系。
求完成 \(N\) 次操作的可能方案数,结果对 \(998244353\) 取模。
\(1\le N\le200\)。保证 \(0\le M\le N(2N-1)\),\(1\le A_i,B_i\le2N\),且所有 \(\{A_i,B_i\}\) 组合均不相同。
注意到移除的过程一定是从里向外扩张。考虑区间 DP,设 \(dp(l,r)\) 表示将 \([l,r]\) 范围内的学生移除的方案数。
初始状态:若 \(i,i+1\) 为好友,则 \(dp(i,i+1)=1\)。
另外,显然 \(dp(l,r)>0\) 仅当 \(r-l+1\) 为偶数。
\(dp(l,r)\) 可能由两种情况转移而来:
- 若 \(l,r\) 互为好友,则最后移除 \(l,r\),贡献为 \(dp(l+1,r-1)\)。
- 从中间断开,枚举断点 \(k\)(\(l+1\le k\le r-1\)),分成两部分 \([l,k]\) 和 \([k+1,r]\),若不考虑移除的顺序,方案数首先为 \(dp(l,k)\cdot dp(k+1,r)\),再考虑到每次可以选择在左侧移除或者在右侧移除,其中左侧共移除 \((k-l+1)/2\) 次,右侧共移除 \((r-k)/2\) 次,两侧共移除 \((r-l+1)/2\) 次,因此再乘上一个 \(\binom{(r-l+1)/2}{(r-k)/2}\)。
根据加法原理,这两种情况再相加即可。
然而我们发现,如果单纯这样转移,可能有重复的情况,因为不同断点的方案可能是相同的。
例如 \(1,2,3,4,5,6\) 相邻的两两为朋友,\(dp(1,6)\) 可能被分成 \(dp(1,2),dp(3,6)\) 和 \(dp(1,4),dp(5,6)\),而 \(dp(3,6)\) 内部和 \(dp(1,4)\) 内部再分成两部分,就导致了方案 \((1,2),(3,4),(5,6)\) 被计算了两次。
考虑类似《进阶指南》中 P10956 金字塔 的做法,以 第一个 极大的区间长度(\(k\))作为阶段进行转移,这样就保证了不重不漏。
综上,状态转移方程为:
其中 \(l,k\) 互为朋友,\(l+1\le k\le r\),且 \(k\) 与 \(l\) 奇偶性不同。
注意:\(k=l+1\) 时以 \([l,l+1]\) 为第一个极大区间,\(dp(l+1,l)\) 贡献为 \(1\);\(k=r\) 时以 \([l,r]\) 为唯一的极大区间,\(dp(r+1,r)\) 贡献也为 \(1\)。因此需要定义 \(dp(i+1,i)=1\)。
#include <bits/stdc++.h>
using namespace std;
int const MOD = 998244353;
int const N = 410;
int dp[N][N], C[N][N];
bool f[N][N];
int ksm(int a, int x) {
int res = 1;
while (x) {
if (x & 1) res = res * 1ll * a % MOD;
a = a * 1ll * a % MOD;
x >>= 1;
}
return res;
}
void solve() {
int n, m;
cin >> n >> m;
C[0][0] = 1;
for (int i = 1; i <= (n << 1); i++) {
dp[i + 1][i] = 1;
C[i][0] = 1;
for (int j = 1; j <= i; j++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
}
}
for (int i = 1, a, b; i <= m; i++) {
cin >> a >> b;
f[a][b] = true;
if (a == b - 1) {
dp[a][b] = 1;
}
}
for (int d = 4; d <= (n << 1); d += 2) {
for (int l = 1, r = d; r <= (n << 1); l++, r++) {
for (int k = l + 1; k <= r; k += 2) {
if (!f[l][k]) continue;
dp[l][r] += 1ll * dp[l + 1][k - 1] * dp[k + 1][r] % MOD * C[d / 2][(r - k) / 2] % MOD;
dp[l][r] %= MOD;
}
}
}
cout << dp[1][n << 1] << '\n';
return;
}
signed main() {
cin.tie(0)->sync_with_stdio(false);
int tt = 1;
// cin >> tt;
while (tt--) solve();
return 0;
}

浙公网安备 33010602011771号