BZOJ4350: 括號序列再戰豬豬俠【區間DP】
Description
括號序列與豬豬俠又大戰了起來。
眾所周知,括號序列是一個只有(和)組成的序列,我們稱一個括號序列S合法,當且僅當:
1.( )是一個合法的括號序列。
2.若A是合法的括號序列,則(A)是合法的括號序列。
3.若A,B是合法的括號序列,則AB是合法的括號序列。
我們考慮match[i]表示從左往右數第i個左括號所對應的是第幾個右括號,現在他得到了一個長度為2n的括號序列,給了你m個資訊,第i個資訊形如ai,bi,表示match[ai]<match[bi],要你還原這個序列。
但是你發現這個豬豬俠告訴你的資訊,可能有多個括號序列合法;甚至有可能告訴你一個不存在合法括號序列的資訊!
你最近學了取模運算,你想知道答案對998244353(7172^23+1)取模的結果,這個模數是一個質數。
Input
第一行一個正整數T,T< = 5,表示資料組數。
對於每組資料,第一行一個n,m,n表示有幾個左括號,m表示資訊數。
接下來m行,每行兩個數ai,bi,1< = ai,bi< = n。
Output
對於每組資料,輸出一個數表示答案。
Sample Input
5
1 0
5 0
3 2
1 2
2 3
3 2
2 1
2 3
3 3
1 2
2 3
3 1
Sample Output
1
42
1
2
0
HINT
對於前兩個點,是卡特蘭數的情況。
對於第三個點,合法的情況只可能是 ()()()。
對於第四個點,合法情況可能是 (()()) 或者 (())()
對於第五個點,由於拓撲關係形成了環,顯然無解。
對於 100% 的資料,保證 n < = 300
思路
考慮區間DP
\(dp_{l,r}\)表示滿足\([l,r]\)的左區間滿足條件的方案數
然後你每次考慮在\([l+1,r]\)的個區間中加入l這個括號
有三種情況:
- 全部包含後面
- 和後面相離
- 把後面分成兩半
然後發現我們要處理出兩個區間分離沒有任何衝突的方案數
這個東西可以對match的二維矩陣做一個字首和sum
然後\([l_1,r_1]\)
#include<bits/stdc++.h>
using namespace std;
const int N = 310;
const int Mod = 998244353;
int f[N][N], p[N][N], q[N][N], sum[N][N];
int a[N][N], b[N][N];
int n, m;
int add(int a, int b) {
return (a += b) >= Mod ? a - Mod : a;
}
int mul(int a, int b) {
return 1ll * a * b % Mod;
}
int calc(int x1, int y1, int x2, int y2) {
return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}
void solve() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
f[i][j] = sum[i][j] = 0;
bool bk = 0;
for (int i = 1; i <= m; i++) {
int x, y; scanf("%d %d", &x, &y);
sum[x][y] = 1;
if (x == y) bk = 1;
}
if (bk) {
printf("0\n");
return;
}
for (int i = 1; i <= n; i++) {
f[i][i] = 1;
for (int j = 1; j <= n; j++) {
sum[i][j] += sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1];
}
}
for (int len = 2; len <= n; len++) {
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if (!calc(l, l + 1, l, r)) f[l][r] = add(f[l][r], f[l + 1][r]);
if (!calc(l + 1, l, r, l)) f[l][r] = add(f[l][r], f[l + 1][r]);
for (int k = l + 1; k <= r - 1; k++) {
if (!calc(l, l + 1, l, k) && !calc(k + 1, l, r, k))
f[l][r] = add(f[l][r], mul(f[l + 1][k], f[k + 1][r]));
}
}
}
printf("%d\n", f[1][n]);
}
int main() {
#ifdef dream_maker
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
int T; scanf("%d", &T);
while (T--) solve();
return 0;
}