博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

Codeforces.314E.Sereja and Squares(DP)

题目链接

http://www.cnblogs.com/TheRoadToTheGold/p/8443668.html

\(Description\)

给你一个擦去了部分左括号和全部右括号的括号序列,括号有25种,用除x之外的小写字母a~z表示。求有多少种合法的括号序列。答案对4294967296取模。
合法序列不能相交,如()[],([])是合法序列,而([)]是不合法的。

\(Solution\)

很重要的一点是,如果当前可以放右括号,那么方案是唯一的(不能相交)。
假设只有一种括号。
\(f[i][j]\)表示\(1\sim i\),还有\(j\)个左括号未匹配的方案数。根据当前是什么转移即可。复杂度\(O(n^2)\)
空间可以压成\(O(n)\)。考虑优化枚举范围,但是\(j\)的下界是\(0/1\),上界是\(\min(i,n/2)\),没啥好优化的。

只能尝试换种状态表示,\(f[i][j]\)表示\(1\sim i\),已经填了\(j\)个右括号的方案数。
则若\(i+1\) 为/可以放 左括号,\(f[i+1][j] =/+= f[i][j]\);若\(i+1\)可以放右括号,则\(f[i+1][j+1] += f[i][j]\)
这样只有一种括号的答案是\(f[n][n/2]\)。假设原串有\(m\)个左括号,则25种括号的答案为\(25^{n/2-m}*f[n][n/2]\)(右括号确定,只需看左括号方案)。
\(j\)的下界是\(\max(1,i-n/2)\),上界可以按\(i/2\)算。平摊复杂度不知道,还是\(n^2\)的。。但跑得很快。
\(4294967296=2^{32}\)取模相当于unsigned int自然溢出。

//1372ms	300KB
#include <cstdio>
#include <algorithm>
typedef unsigned int uint;
const int N=1e5+5;

char s[N];
uint f[N];

int main()
{
	int n; scanf("%d",&n);
	if(n&1) return putchar('0'),0;
	scanf("%s",s+1);

	f[0]=1; int m=0, n2=n>>1;
	for(int i=1; i<=n; ++i)
		if(s[i]=='?')
			for(int j=i>>1,lim=std::max(1,i-n2); j>=lim; --j)
				f[j]+=f[j-1];
		else ++m;
	if(n2<m) return putchar('0'),0;
	for(int i=n2-m; i--; ) f[n2]*=25;
	printf("%u",f[n2]);

	return 0;
}
posted @ 2018-07-26 17:38  SovietPower  阅读(304)  评论(0编辑  收藏  举报