【bzoj4350】括号序列再战猪猪侠 区间DP
2016-03-11 15:07
344 查看
Description
括号序列与猪猪侠又大战了起来。众所周知,括号序列是一个只有(和)组成的序列,我们称一个括号
序列S合法,当且仅当:
1.( )是一个合法的括号序列。
2.若A是合法的括号序列,则(A)是合法的括号序列。
3.若A,B是合法的括号序列,则AB是合法的括号序列。
我们考虑match[i]表示从左往右数第i个左括号所对应的是第几个右
括号,现在他得到了一个长度为2n的括号序列,给了你m个信息,第i
个信息形如ai,bi,表示match[ai]
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
Source
ydc给我们的考试题…T1…………ydc:这道题是本场考试最水的
具体做法是区间dp,状态转移方程也挺好写,三种情况:()AB,(AB),(A)B。
重点在如何处理【i的右括号必须在j的右括号左边】这个约束。
二维前缀和。sum[i][j]为1表示i必须在j之前。考虑一个矩形所代表的含义:
先考虑边长为1的矩形。矩形((a,b),(a,c))的权值若是1,则表示a必须在b~c中某个的前面。若为0,则表示a没必要在b~c的任意一个数左边,也就是a的右括号可以放c后面或者b前面。
我们发现矩形权值为0的性质可以利用。考虑((a,b),(c,d))的含义:若权值为0,则表示a~c中任意一个元素对于b~d中任意一个元素都没有条件约束。这个性质可以快速查询一个括号序列是否可以在另一个括号序列之前。
状态dp[i][j]表示第i个左括号到第j个左括号组成的括号序列方案数,然后根据二维前缀和查询DP就行了…
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int SZ = 500; const int mod = 998244353; int dp[SZ][SZ]; int sum[SZ][SZ]; int getsum(int a,int b,int c,int d) { return sum[c][d] - sum[c][b - 1] - sum[a - 1][d] + sum[a - 1][b - 1]; } int ask(int n) { for(int i = 1;i <= n;i ++) if(getsum(i,i,i,i)) return 0; for(int i = n;i >= 1;i --) { dp[i][i] = 1; for(int j = i + 1;j <= n;j ++) { if(!getsum(i,i + 1,i,j)) //(AB) dp[i][j] = (dp[i][j] + dp[i + 1][j]) % mod; if(!getsum(i + 1,i,j,i)) //()AB dp[i][j] = (dp[i][j] + dp[i + 1][j]) % mod; for(int k = i + 1;k < j;k ++) //(A)B if(!getsum(i,i + 1,i,k) && !getsum(k + 1,i,j,i) && !getsum(k + 1,i + 1,j,k)) dp[i][j] = (dp[i][j] + (LL)dp[i + 1][k] * dp[k + 1][j] % mod) % mod; } } return dp[1] ; } void init() { memset(dp,0,sizeof(dp)); memset(sum,0,sizeof(sum)); } int main() { int T; scanf("%d",&T); while(T --) { init(); int n,m; scanf("%d%d",&n,&m); for(int i = 1;i <= m;i ++) { int x,y; scanf("%d%d",&x,&y); sum[x][y] = 1; } for(int i = 1;i <= n;i ++) for(int j = 1;j <= n;j ++) sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]; printf("%d\n",ask(n)); } return 0; }
相关文章推荐
- 人人网移动开发架构
- JAVA8 十大新特性详解
- 给Eclipse提速的7个技巧
- 多表关联汇总
- Android Https相关完全解析 当OkHttp遇到Https
- 转容器内存释放问题
- 了解和熟悉操作系统
- 如何让页面只能在微信端打开?
- SQL Server 触发器
- ASP.NET反射
- start_kernel到init进程启动的过程
- 信息系统实践手记2-客户端启动速度调优思路
- /ctlutil.h(278) : error C4430: missing type specifier - int assumed.
- linux内核之 phys_to_virt
- 在Linux下启动Oracle
- python 网络编程攻略第一章 服务器/客户端交互栗子
- ABP框架理论学习之Hangfire集成
- 个人简介
- 了解和熟悉操作系统
- 实验0、了解和熟悉操作系统