牛客普及组-合法括号序列

题目大意

键盘上有左括号(,右括号),和退格键-,共三个键。

牛牛希望按键n次,使得输入的字符串恰好一个合法的括号序列。

每按一次左括号(,字符串末尾追加一个左括号(

每按一次右括号),字符串末尾追加一个右括号)

每按一次退格键-,会删掉字符串的最后一个字符,

特别的,如果字符串为空,牛牛也可以按退格,但是什么都不会发生。

合法括号序列的定义和上一场比赛中的C题是一样的
https://www.nowcoder.com/acm/contest/164/C

输出方案数对p取模,注意p可能不是质数。

注:只要按键方法不同,就是不同的方案,即使得到的序列一样。


 此题有一个比较麻烦的地方就是需要把删除的贡献也考虑在内

不妨将当前括号序列长度考虑在内

设 g[i][j] 表示进行了 i 次操作后序列长度为 j 的方案数

转移也很好想 : g[i][j] = g[i -1][j - 1] + g[i - 1][j + 1] * 2

乘以 2 就是填入左 / 右括号,j = 0 时把 j - 1 改成 0

由于统计答案时统计的是合法括号序,就再 dp 出长度为 i 时的合法括号序即可

状态设计为 f[i][j] 表示当前长度为 i ,还有 j 个左括号未匹配


代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
using namespace std;

typedef long long ll;
const int MAXN = 1005;

int n;
ll f[MAXN][MAXN], g[MAXN][MAXN];
ll p, ans;

int main() {
	scanf("%d%lld", &n, &p);
	f[0][0] = 1ll;
	for(int i = 1; i <= n; ++i) {
		f[i][0] = f[i - 1][1];
		for(int j = 1; j <= i; ++j) {
			f[i][j] = (f[i][j] + f[i - 1][j + 1] + f[i - 1][j - 1]) % p;
		}
	}
	g[0][0] = 1ll;
	for(int i = 1; i <= n; ++i) {
		for(int j = 0; j <= i; ++j) {
			g[i][j] = (g[i - 1][max(j - 1, 0)] + g[i - 1][j + 1] * 2) % p;
		}
	}
	ans = g[n][0];
	for(int j = 2; j <= n; j += 2) {
		ans = (ans + f[j][0] * g[n][j]) % p;
	}
	printf("%lld\n", ans);
	return 0;
}

 因为合法的括号序长度一定是偶数,所以枚举长度也枚举偶数

 

posted @ 2018-09-17 14:12  EvalonXing  阅读(357)  评论(0编辑  收藏  举报