题解 LGP7914【[CSP-S 2021] 括号序列】

当年这个题是 0 分,连区间 DP 都没看出来。

problem

小 w 在赛场上遇到了这样一个题:一个长度为 \(n\) 且符合规范的括号序列,其有些位置已经确定了,有些位置尚未确定,求这样的括号序列一共有多少个。

身经百战的小 w 当然一眼就秒了这题,不仅如此,他还觉得一场正式比赛出这么简单的模板题也太小儿科了,于是他把这题进行了加强之后顺手扔给了小 c。

具体而言,小 w 定义“超级括号序列”是由字符 ()* 组成的字符串,并且对于某个给定的常数 \(k\),给出了“符合规范的超级括号序列”的定义如下:

  1. ()(S) 均是符合规范的超级括号序列,其中 S 表示任意一个仅由不超过 \(k\) 字符 * 组成的非空字符串(以下两条规则中的 S 均为此含义);
  2. 如果字符串 AB 均为符合规范的超级括号序列,那么字符串 ABASB 均为符合规范的超级括号序列,其中 AB 表示把字符串 A 和字符串 B 拼接在一起形成的字符串;
  3. 如果字符串 A 为符合规范的超级括号序列,那么字符串 (A)(SA)(AS) 均为符合规范的超级括号序列。
  4. 所有符合规范的超级括号序列均可通过上述 3 条规则得到。

例如,若 \(k = 3\),则字符串 ((**()*(*))*)(***) 是符合规范的超级括号序列,但字符串 *()(*()*)((**))*)(****(*)) 均不是。特别地,空字符串也不被视为符合规范的超级括号序列。

现在给出一个长度为 \(n\) 的超级括号序列,其中有一些位置的字符已经确定,另外一些位置的字符尚未确定(用 ? 表示)。小 w 希望能计算出:有多少种将所有尚未确定的字符一一确定的方法,使得得到的字符串是一个符合规范的超级括号序列?

对于 \(100 \%\) 的数据,\(1 \le k \le n \le 500\)

solution

最终括号串形如:(***(...)(...)***(...)),或者 ((...)(...)***(...)***),或者 ((...)(...)***(...)),就是说中间可有可无,两边只留一个。

\(st_{l,r}\) 表示 \([l,r]\) 是否可以全为星号。

\(f_{l,r}\) 表示 (...)***(...)...(...)(...),强制两边都是括号,转移等会。

\(g_{l,r}\) 表示 (...),就是外面套一层括号之后里面合法的方案数。

\(f_{l,r}=\sum_{l\leq k\leq r}g_{l,k}\left(f_{k+1,r}+\sum_{k<p<r}st_{k+1,p}f_{p+1,r}\right).\)

\(g_{l,r}=[a_l='('][a_r=')'](f_{l+1,r-1}+\sum_{l+1\leq k\leq r-1}st_{l+1,k}f_{k+1,r-1}+\sum_{l+1<k\leq r-1}f_{l+1,k-1}st_{k,r-1}).\)

这两个东西巨大多细节因此我决定用代码的形式展现!

点击查看代码
LL F(int l,int r){
	if(l>r) return 1;
	if(l==r) return 0;
	LL &res=f[l][r]; if(~res) return res;
	res=0;
	for(int k=l;k<=r;k++){
		LL glk=G(l,k),tot=0;
		for(int p=k;p<r&&glk;p++){
			if(!st[k+1][p]) break;
			red(tot+=F(p+1,r));
		}
		red(res+=tot*glk);
	}
	red(res+=G(l,r));
	return res;
}
LL G(int l,int r){
	if(!cmp(s[l],'(')||!cmp(s[r],')')) return 0;
	if(l>=r) return 0;
	LL &res=g[l][r]; if(~res) return res;
	l++,r--,res=F(l,r);
	if(l<=r){
		for(int k=l;k<=r;k++) red(res+=st[l][k]*F(k+1,r));
		for(int k=l;k<=r;k++) red(res+=st[k][r]*F(l,k-1));
		red(res-=st[l][r]);
	}
	return res;
}

考虑 \(f\) 那个四重循环顶不住,考虑枚举完 \(g\) 的长度后,令 \(h(l,r)\) 表示,有多少种方案形如 \(f\) 的左边接上正整数个星号。然后就做完了!!!

code

点击查看代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+7; 
int n,k;
char s[510];
LL f[510][510],g[510][510],h[510][510],st[510][510];
LL F(int l,int r);
LL G(int l,int r);
LL H(int l,int r);
void red(LL&x){x=(x%P+P)%P;}
bool cmp(char a,char b){
	if(a=='?'||b=='?') return 1;
	return a==b;
}
void print(int l,int r){
	for(int i=l;i<=r;i++) debug("%c",s[i]);
	debug("\n");
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	memset(f,-1,sizeof f),memset(g,-1,sizeof g),memset(h,-1,sizeof h);
	scanf("%d%d%s",&n,&k,s+1);
	for(int i=1;i<=n;i++){
		st[i][i-1]=1;
		for(int j=i;j<=n;j++) st[i][j]=st[i][j-1]&&cmp(s[j],'*')&&j-i+1<=k;
	}
	debug("%s\n",s+1);
	for(int i=1;i<=n;i++) debug("%d",i);
	debug("\n");
	printf("%lld\n",F(1,n));
	return 0;
}
LL H(int l,int r){//f 左边接正整数个星号 
	if(l>r) return 0;
	if(l==r) return 0;
	LL &res=h[l][r]; if(~res) return res;
	res=0;
	for(int k=l;k<r;k++){
		if(!st[l][k]) break;
		red(res+=F(k+1,r));
	}
	debug("h[%d][%d]=%lld ",l,r,h[l][r]),print(l,r);
	return res;
}
LL F(int l,int r){
//	debug("call f(%d,%d)\n",l,r);
	if(l>r) return 1;
	if(l==r) return 0;
	LL &res=f[l][r]; if(~res) return res;
	res=0;
	for(int k=l;k<r;k++){
		LL glk=G(l,k),tot=0;
//		for(int p=k;p<r&&glk;p++){
//			if(!st[k+1][p]) break;
//			red(tot+=F(p+1,r));
//		}
		red(tot=F(k+1,r)+H(k+1,r));
		red(res+=tot*glk);
	}
	red(res+=G(l,r));
	debug("f[%d][%d]=%lld ",l,r,f[l][r]),print(l,r);
	return res;
}
LL G(int l,int r){
//	debug("call g(%d,%d)\n",l,r);
	if(!cmp(s[l],'(')||!cmp(s[r],')')) return 0;
	if(l>=r) return 0;
	LL &res=g[l][r]; if(~res) return res;
	l++,r--,res=F(l,r);
	if(l<=r){
		for(int k=l;k<=r;k++) red(res+=st[l][k]*F(k+1,r));
		for(int k=l;k<=r;k++) red(res+=st[k][r]*F(l,k-1));
		red(res-=st[l][r]);
	}
	debug("g[%d][%d]=%lld ",l-1,r+1,res),print(l-1,r+1);
	return res;
}

posted @ 2022-11-23 22:37  caijianhong  阅读(56)  评论(0编辑  收藏  举报