Description

括号序列与猪猪侠又大战了起来。
众所周知,括号序列是一个只有(和)组成的序列,我们称一个括号
序列S合法,当且仅当:
1.( )是一个合法的括号序列。
2.若A是合法的括号序列,则(A)是合法的括号序列。
3.若A,B是合法的括号序列,则AB是合法的括号序列。
我们考虑match[i]表示从左往右数第i个左括号所对应的是第几个右
括号,现在他得到了一个长度为2n的括号序列,给了你m个信息,第i
个信息形如ai,bi,表示match[ai]<match[bi],要你还原这个序列。
但是你发现这个猪猪侠告诉你的信息,可能有多个括号序列合法;甚
至有可能告诉你一个不存在合法括号序列的信息!
你最近学了取模运算,你想知道答案对998244353(7*17*2^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
题解:如果match[ai]<match[bi],那么为了序列能合法,若ai<bi,ai所对应的右括号也一定在bi的左边,若ai>bi,ai和它所对应的右括号,一定被bi所对应的的括号包在中间,我们来考虑一下括号有几种可能吧,很显然有(AB),()AB,{AB()可看做同种情况},(A)B,这三种方案,我们枚举一个区间的左括号,f[i][j]表示第i个左括号到第j个左括号能形成多少种方案。
   1.如果要符合上面的第一种方案,那么很明显第i个左括号和它对应的右括号不能完全在AB的左边,即第i个左括号对第i+1到j之间的左括号不能有任何一个被提出过match[i]<match[i+1~j],如果符合的话我们就加上f[i+1][j]。
   2.如果要符合第二种方案,那么第i个左括号和它对应的右括号必须完全在AB的左边,即第i个左括号对第i+1到j之间的左括号必须每个都被提出过match[i]<match[i+1~j],等同于第i+1~j之间的括号对第i个括号没有被提出过match[i+1~j]<match[i],如果符合的话我们就加上f[i+1][j]。
   3.如果要符合第三种我们就要进行枚举了,因为我们不知道B序列最右边的左括号是总序列中的第几个括号。枚举一个k来表示A中的最后一个左括号,则B中的开头左括号为k+1,我们要如何满足这种条件呢?有两个注意点,(1).k+1到j的括号必须全部都在i~k括号的右边。(2).i+1~k括号必须包含在第i个括号中,即第i个括号不能在他们的右边。
只要满足注意点,它的方案数就可加上两边的乘积。
最最重要的一点来了,我们如何去判断括号在左边右边呢?不要说去一个个枚举。。。我们用一个s数组来储存关系,若match[ai]<match[bi],则s[ai][bi]赋值为1,反之,赋值为0,假设我们现在需要第a~b个左括号全部都在第c~d个括号包括对应的右括号的右边就是说任何一个s[ai~bi][ci~di]都需要为0,那么实际上我们只要知道一个左上角为a,b,右下角为c,d的矩阵中的元素为0,就可以了。那么判断的时候我们只需要用矩阵前缀和就可以用O(1)的复杂度算出矩阵的值从而判断左右关系。
大概就是这样了,具体程序看吧。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int j,n,m,t,x,y,s[][];
long long f[][];//一定要记得开long long,否则。。。
int kk=;
int check(int a,int b,int c,int d)
{
return (s[c][d]-s[c][b-]-s[a-][d]+s[a-][b-]);
}
void work()
{
memset(s,,sizeof(s));//记得清空
memset(f,,sizeof(f));
cin>>n>>m;
for (int i=;i<=m;i++)
{ cin>>x>>y;
s[x][y]=;//记录对应的关系
}
for (int i=;i<=n;i++)
for (int j=;j<=n;j++)
s[i][j]=s[i][j]+s[i-][j]+s[i][j-]-s[i-][j-];//矩阵前缀和
for (int i=;i<=n;i++)
if (check(i,i,i,i)==)//如果自身对自身有所限制的话,那么这种情况是不存在的,可以直接输出0.
{
printf("0\n");
return;
}
for (int i=;i<=n;i++) f[i][i]=;//构成单个括号的可能性只有一种 for (int len=;len<=n;len++)
for (int i=;i<=n-len+;i++)
{
j=i+len-;
if (check(i,i+,i,j)==) f[i][j]=(f[i][j]+f[i+][j])%kk;
if (check(i+,i,j,i)==) f[i][j]=(f[i][j]+f[i+][j])%kk;
for (int k=i+;k<=j-;k++)
if ((check(k+,i,j,k)==)&&(check(i,i+,i,k)==))
f[i][j]=(f[i][j]+f[i+][k]*f[k+][j]%kk)%kk;//这个程序中解释过了
}
cout<<f[][n]<<endl;
}
int main()
{
cin>>t;
for (int i=;i<=t;i++) //多组输入数据
work();
return ;
}

最新文章

  1. 给自己的Unity添加声音文件
  2. Android学习三:线程操作
  3. p2p研究
  4. jquery eq 用法
  5. 反弹SHELL汇总
  6. mp3文件 ID3v2 帧标识的含义
  7. Android加载网络图片的工具类
  8. root密码
  9. iOS开发--线程通信
  10. How to fix “Duplicate sources.list entry …” issue
  11. Android 内核初识(8)Binder
  12. mysql统计表的大小
  13. C#子线程更新UI控件的方法总结
  14. C++虚函数及虚函数表解析
  15. haskell,lisp,erlang你们更喜欢哪个?
  16. 《Hadoop》对于高级编程Hadoop实现构建企业级安全解决方案
  17. android 音频播放总结 soundlPool,MediaPlay
  18. python 面对post分页爬虫
  19. postman工具的使用
  20. kafka问题集锦

热门文章

  1. DelphiXE2 DataSnap开发技巧收集
  2. BZOJ2038: [2009国家集训队]小Z的袜子(hose)
  3. Chrome浏览器Network面板http请求时间分析
  4. Reveal的使用及破解方法
  5. 把java对象转化为json格式的对象数组
  6. nullcon HackIM 2016 -- Crypto Question 1
  7. [Docker] docker 基础学习笔记3(共6篇)
  8. jdk环境变量的配置并检测是否配置成功
  9. iOS中的CocoaPods用法及常用命令
  10. Git学习(二)——创建版本库、查看与回退版本