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

转载于:https://www.cnblogs.com/dream-maker-yk/p/9922012.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值