总结收获
题目
- 传送门
- 题意:
- N表示人的个数为2*N,M表示不同的好朋友的对数。(N<=200)
- 最开始2N个人排列成1,2,…,2N,每次操作可以去掉一对相邻的好朋友,中间空隙补上,求能把所有人都去掉的次数总和。
- 两次不同:存在 1 ≤ i ≤ N 1\le i\le N 1≤i≤N,使得这两次在第 i 次操作去掉两个人不完全相同。
![在这里插入图片描述](https://img-blog.csdnimg.cn/06e61d70a3a04c7d8c7ebcdeb190e343.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBASV9oYXZlX2Ffd29ybGQ=,size_16,color_FFFFFF,t_70,g_se,x_16)
- 题解:首先
dp[i][j]
表示取完区间i~j
所有的数的不同操作顺序种数。
- 很像括号匹配,所以就是区间DP,然后现在考虑怎么转移;
- 在求区间
[i,j]
的时候,所有小的区间的dp
值都已经知道了,又要去重,又要正确转移,先考虑怎样不重复的枚举,然后再考虑每一步怎样转移:
- 枚举和
i
匹配的数,首先i~i+1
或者i~j+1
的时候很好操作,考虑其他i~k
匹配。
- 两个区间,取数顺序一定,那么排列种数为
C(len1+len2,len1)
即在len1+len2
中取len1
个位置给前面的区间,然后乘上他们的顺序种数即可。
- 代码:
#include <bits/stdc++.h>
#define int long long
#define ll long long
#define pii pair<int, int>
#define dbg(x) cout << #x << "===" << x << endl
using namespace std;
const int N = 1e3 + 10;
const int mod = 998244353;
int n, m;
int a, b;
int dp[N][N], mp[N][N];
int Len(int l, int r) {
return (r - l + 1) / 2; }
int re[N], inv[N], fac[N];
void init(int n) {
re[0] = inv[1] = fac[0] = re[1] = fac[1] = 1;
for (int i = 2; i <= n; ++i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
re[i] = re[i - 1] * inv[i] % mod;
}
}
int C(int a, int b) {
return fac[a] * re[b] % mod * re[a - b] % mod; }
signed main() {
cin >> n >> m;
init(2 * n);
for (int i = 1; i <= m; i++) {
scanf("%lld%lld", &a, &b);
if (a > b) swap(a, b);
mp[a][b] = 1;
}
for (int len = 2; len <= 2 * n; len += 2) {
for (int i = 1; i <= 2 * n; i++) {
int j = i + len - 1;
if (j > 2 * n) break;
if (len == 2)
dp[i][j] = mp[i][j];
else {
if (mp[i][i + 1] && dp[i + 2][j])
dp[i][j] =
(dp[i][j] + (Len(i + 2, j) + 1) * dp[i + 2][j] % mod) %
mod;
for (int k = i + 3; k <= j - 2; k += 2) {
if (mp[i][k] && dp[i + 1][k - 1] && dp[k + 1][j]) {
dp[i][j] =
(dp[i][j] + C(Len(i + 1, k - 1) + 1 + Len(k + 1, j),
Len(k + 1, j)) *
dp[i + 1][k - 1] % mod *
dp[k + 1][j] % mod) %
mod;
}
}
if (mp[i][j] && dp[i + 1][j - 1])
dp[i][j] = (dp[i][j] + dp[i + 1][j - 1] % mod) % mod;
}
}
}
cout << dp[1][2 * n] << endl;
return 0;
}