[P7914] [CSP-S 2021] 括号序列
不要试着拿这道题的暴力分。
状态的设置
看了题面,我们发现有三个变量:长度,左端点,星号个数,那我们先不妨开三维的 DP 数组
- 时间复杂度高达
\mathcal{O}(n^6) 。 - 完全没必要加上第三维。
那我们想办法消掉第三维,用
状态转移方程初版
挨个情况的考虑,这里设
设当前区间为
写完这些我们有两个问题:
- 我们的算法真的正确吗?
- 如何求出
\text{condition} ?
对于第一个问题,样例
对于第二个问题,我们可以定义前缀和,求和的是
去重
为什么我们会重复的算一个答案,原因是有很多答案可以通过多种方式组合起来,比如
我们不妨改一下我们原本的定义,经过一番思考,我们可以发现存在多解是由于
至此我们的去重工作就完成了,但是我们的时间复杂度仍然是
算法优化
我们仔细观察
如果你是 TLE 95 分,检查一下你是不是取模取多了,减少取模的式子可以有效减少时间。
细节
计算
对于所有的情况都要想清楚
最终输出一定要取模。
初始化是对于所有的问号和括号,看他下一位是否可能匹配,并进行初始化,单独的星号是没法成为合法括号序列的,不用管它。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=5e2+5;
int n,k,dp[N][N][3],d[N];
char s[N];
signed main(){
scanf("%lld %lld",&n,&k);
scanf("%s",s+1);
for(register int i=1;i<=n;i++){
d[i]=d[i-1];
if((s[i]=='('||s[i]=='?')&&(s[i+1]==')'||s[i+1]=='?')) dp[i][i+1][0]=1;
if(s[i]=='('||s[i]==')') d[i]=d[i-1]+1;
}
for(register int i=2;i<=n;i++){
for(register int l=1;l<=n;l++){
int r=l+i-1;
if(r>n) break;
if(r-l>=2&&(s[l]=='('||s[l]=='?')&&(s[r]==')'||s[r]=='?')){
dp[l][r][0]=((dp[l][r][0]+dp[l+1][r-1][1])%mod+dp[l+1][r-1][0])%mod;//情况(A)
}
if(r-l>=2&&((d[r-1]-d[l])==0)&&(s[l]=='('||s[l]=='?')&&(s[r]==')'||s[r]=='?')&&r-l-1<=k){//情况(S)
dp[l][r][0]=(dp[l][r][0]+((d[r-1]-d[l])==0))%mod;
}
if(r-l>=3){
for(register int p=l;p<r;p++){
dp[l][r][1]=((dp[l][p][0]%mod*(dp[p+1][r][0]+dp[p+1][r][1])%mod)%mod+dp[l][r][1])%mod;//情况AB
}
}
if(r-l>=4&&(s[l]=='('||s[l]=='?')&&(s[r]==')'||s[r]=='?')){
for(register int p=r-k-1;p<r-1;p++){//情况(AS)
if(r-p-1<=k) dp[l][r][0]=(dp[l][r][0]+(dp[l+1][p][0]+dp[l+1][p][1])*((d[r-1]-d[p])==0))%mod;
}
}
if(r-l>=4&&(s[l]=='('||s[l]=='?')&&(s[r]==')'||s[r]=='?')){
for(register int p=l+1;p<=min(l+k,r-1);p++){//情况(SA)
if(p-l<=k) dp[l][r][0]=(dp[l][r][0]+(dp[p+1][r-1][0]+dp[p+1][r-1][1])*((d[p]-d[l])==0))%mod;
}
}
if(r-l>=2){//对于 ASB 串的优化
for(register int p=l;p<=min(l+k-1,r);p++){
dp[l][r][2]=(dp[l][r][2]+(dp[p+1][r][0]+dp[p+1][r][1])%mod*((d[p]-d[l-1])==0))%mod;
}
}
if(r-l>=4){
for(register int p=l+1;p<r-1;p++){
dp[l][r][1]=(dp[l][r][1]+(dp[l][p][0]%mod*dp[p+1][r][2]%mod)%mod)%mod;
}
}
}
}
printf("%lld",(dp[1][n][0]+dp[1][n][1])%mod);
}