[牛客网NOIP赛前集训营-普及组(第二场)]D-合法括号序列
链接:https://www.nowcoder.com/acm/contest/165/D
来源:牛客网
合法括号序列
键盘上有左括号(,右括号),和退格键-,共三个键。
牛牛希望按键n次,使得输入的字符串恰好一个合法的括号序列。
每按一次左括号(,字符串末尾追加一个左括号(
每按一次右括号),字符串末尾追加一个右括号)
每按一次退格键-,会删掉字符串的最后一个字符,
特别的,如果字符串为空,牛牛也可以按退格,但是什么都不会发生。
合法括号序列的定义和上一场比赛中的C题是一样的
https://www.nowcoder.com/acm/contest/164/C
键盘上有左括号(,右括号),和退格键-,共三个键。
牛牛希望按键n次,使得输入的字符串恰好一个合法的括号序列。
每按一次左括号(,字符串末尾追加一个左括号(
每按一次右括号),字符串末尾追加一个右括号)
每按一次退格键-,会删掉字符串的最后一个字符,
特别的,如果字符串为空,牛牛也可以按退格,但是什么都不会发生。
合法括号序列的定义和上一场比赛中的C题是一样的
https://www.nowcoder.com/acm/contest/164/C
输出方案数对p取模,注意p可能不是质数。
注:只要按键方法不同,就是不同的方案,即使得到的序列一样。
输入描述:
输入一行两个整数n, p
输出描述:
输出一行一个整数表示答案。
备注:
对于所有数据: 2 <= n <= 1000, 2 <= p <= 10000
30分: n <= 40
70分: n <= 100
我们发现给你一个字符串S,让你求出有多少种以题目中要求的方式拼成S,发现这个是只与字符串长度有关的,与S具体是什么无关。
那么我们是设$f_{ij}$表示按i次,按出的长度为j的方案数。
于是有
$$f[i,j] = f[i-1][j-1] + 2 \times f[i-1][j+1]$$
意思是,我们可以从$j-1$长度的字符串加上$S[j]$变成长度为$j$的。
如果按退格,那么可以删掉第$j+1$的字符,可以删的有两种所以乘2.
然后我们算出来长度为$i$的方案数,只需要算出长度为$i$的合法序列的数量,然后两个相乘就得到了答案,后者是一个简单的DP。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define reg register inline int read() { int res=0;char ch=getchar();bool fu=0; while(!isdigit(ch)){if(ch=='-')fu=1;ch=getchar();} while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=getchar(); return fu?-res:res; } int n, mod; int ans; int f[1005][1005]; int g[1005][1005]; int main() { n = read(), mod = read(); f[0][0] = 1; for (reg int i = 1 ; i <= n ; ++ i) { f[i][0] = f[i - 1][1]; for (reg int j = 1 ; j <= i ; ++ j) f[i][j] = (f[i][j] + f[i - 1][j - 1] + f[i - 1][j + 1]) % mod; } g[0][0] = 1; for (reg int i = 1 ; i <= n ; ++ i) for (reg int j = 0 ; j <= i ; ++ j) g[i][j] = (g[i][j] + g[i-1][max(j - 1, 0)] + 2 * g[i - 1][j + 1]) % mod; for (reg int l = 0 ; l * 2 <= n ; l ++) ans = (ans + f[l*2][0] * g[n][l*2]) % mod; cout << ans << endl; return 0; }