1. 程式人生 > >BZOJ4350: 括號序列再戰豬豬俠【區間DP】

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這個括號

有三種情況:

  1. 全部包含後面
  2. 和後面相離
  3. 把後面分成兩半

然後發現我們要處理出兩個區間分離沒有任何衝突的方案數

這個東西可以對match的二維矩陣做一個字首和sum

然後\([l_1,r_1]\)

\([l_2,r_2]\)的衝突個數就是\(l_1,r_1\)~\(l_2, r_2\)子矩陣的和


#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;
}