P7914 [CSP-S 2021] 括号序列

题目

P7914 [CSP-S 2021] 括号序列

分析

考场上同样傻逼了,居然没调出来,赛后发现两个数组是可行的,考场代码改改加个后缀和就过了。/tuu

题目直接线性dp发现并不好转移,然后 \(n=500\) 一脸 \(n^3\) ,于是可以考虑区间 \(dp\)

然后常规设状态 \(dp[l,r]\) 表示 \([l,r]\) 满足题意的方案数。

然后照着题意模拟 \(dp\) 即可。

接下来会发现其实 \(AB,ASB\) 会算重。

比如:

6 1
()()()

显然应该输出 1。

然后我们考虑单独拿出来这部分的转移,\(AB,ASB\) 的情况只在第一个断点转移,这样就合法了。

于是记一个 \(dp1\) 表示可以用后两者转移的答案,修改 \(dp\) 的含义为:不可以用后两者转移的答案。

接下来发现转移其实是 \(O(n^4)\) 的,因为 \(ASB\) 需要枚举两个断点。

但是这个显然是可以后缀和优化的,于是复杂度 \(O(n^3)\) ,具体见代码。

代码

#include<bits/stdc++.h>
using namespace std;
template<typename T>
void read(T &x){
	x=0;bool f=false;char ch=getchar();
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
const int N=505,M=2e5+5,INF=1e9+7,MOD=1e9+7;
int n,K,dp[N][N],pre[N],dp1[N][N],R[N],sum[N][N];
char str[N];
bool Checkleft(char x){
	if(x=='(') return true;
	if(x=='?') return true;
	return false;
}
bool Checkright(char x){
	if(x==')') return true;
	if(x=='?') return true;
	return false;
}
bool Checks(int l,int r){
	if(r-l+1>K) return false; 
	int res=pre[r]-pre[l-1];
	if(res==r-l+1) return true;
	return false;
}
bool Checkss(char x){return (x=='*'||x=='?');}
inline void incc(int &x,int y){x+=y;if(x>=MOD)x-=MOD;return ;}
inline int dec(int x,int y){x-=y;if(x<0) x+=MOD;return x;}
inline int inc(int x,int y){x+=y;if(x>=MOD)x-=MOD;return x;}
signed main(){
	// freopen("bracket.in","r",stdin);
	// freopen("bracket.out","w",stdout);
	read(n),read(K);
	scanf("%s",str+1);
	for(int i=1;i<=n;i++) pre[i]=pre[i-1]+(str[i]=='*'||str[i]=='?');
	for(int i=n;i>=1;i--){
		if(Checkss(str[i])&&Checkss(str[i+1])) R[i]=R[i+1];
		else if(Checkss(str[i])) R[i]=i;
		else R[i]=0;
		R[i]=min(R[i],i+K-1);
	}
	for(int len=2;len<=n;len++){
		for(int l=1;l+len-1<=n;l++){
			int r=l+len-1;
			if(len>=4){if(Checkleft(str[l])&&Checkright(str[r])) incc(dp[l][r],dp1[l+1][r-1]);}//(A)
			if(len>=5){
				for(int k=l+1;k<=r-3;k++){//(SA)
					if(Checkleft(str[l])&&Checkright(str[r])&&Checks(l+1,k)) incc(dp[l][r],dp1[k+1][r-1]);
				}
				for(int k=r-1;k>=l+3;k--){//(AS)
					if(Checkleft(str[l])&&Checkright(str[r])&&Checks(k,r-1)) incc(dp[l][r],dp1[l+1][k-1]);
				}
			}
			if(len>=4){//AB
				for(int k=l+1;k<=r-2;k++) incc(dp1[l][r],1ll*dp[l][k]*dp1[k+1][r]%MOD);
			}
			if(len>=5){//ASB
				for(int k1=l;k1<=r-3;k1++){
					int rval=min(R[k1+1]+2,r);
					if(!R[k1+1]) continue;
					incc(dp1[l][r],1ll*dp[l][k1]*dec(sum[min(k1+2,r)][r],sum[rval][r])%MOD);
//					for(int k2=k1+2;k2<=r-1;k2++){
//						if(Checks(k1+1,k2-1)) incc(dp1[l][r],1ll*(dp[l][k1])*(dp1[k2][r])%MOD);
//					}
				}
			}
			if(len==2){//()
				if(Checkleft(str[l])&&Checkright(str[r])) incc(dp[l][r],1);
			}
			if(len>=3){//(S)
				if(Checkleft(str[l])&&Checkright(str[r])&&Checks(l+1,r-1)) incc(dp[l][r],1);
			}
			incc(dp1[l][r],dp[l][r]);
		}
		for(int i=1;i<=n;i++) sum[i][i]=0;
		for(int len=2;len<=n;len++){
			for(int l=1;l+len-1<=n;l++){
				int r=l+len-1;
				sum[l][r]=inc(sum[l+1][r],dp1[l][r]);
			}
		}
	}
	printf("%d",dp1[1][n]);
	return 0;
}
posted @ 2021-10-27 20:45  __Anchor  阅读(102)  评论(0)    收藏  举报