题解 LGP7914【[CSP-S 2021] 括号序列】
当年这个题是 0 分,连区间 DP 都没看出来。
problem
小 w 在赛场上遇到了这样一个题:一个长度为 \(n\) 且符合规范的括号序列,其有些位置已经确定了,有些位置尚未确定,求这样的括号序列一共有多少个。
身经百战的小 w 当然一眼就秒了这题,不仅如此,他还觉得一场正式比赛出这么简单的模板题也太小儿科了,于是他把这题进行了加强之后顺手扔给了小 c。
具体而言,小 w 定义“超级括号序列”是由字符 (
、)
、*
组成的字符串,并且对于某个给定的常数 \(k\),给出了“符合规范的超级括号序列”的定义如下:
()
、(S)
均是符合规范的超级括号序列,其中S
表示任意一个仅由不超过 \(k\) 个字符*
组成的非空字符串(以下两条规则中的S
均为此含义);- 如果字符串
A
和B
均为符合规范的超级括号序列,那么字符串AB
、ASB
均为符合规范的超级括号序列,其中AB
表示把字符串A
和字符串B
拼接在一起形成的字符串; - 如果字符串
A
为符合规范的超级括号序列,那么字符串(A)
、(SA)
、(AS)
均为符合规范的超级括号序列。 - 所有符合规范的超级括号序列均可通过上述 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;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-P7914.html