[BZOJ4350] 括号序列再战猪猪侠 题解

我们设 \(dp_{i,j}\) 表示第 \(i\) 到第 \(j\) 个括号合并为序列且最外层不是括号 \(i\) 的可能性,\(f_{i,j}\) 表示最外层是括号 \(i\) 的可能性。则有:

\[\begin{cases} dp_{i,j}=\sum f_{i,k}(dp_{k+1,j}+f_{k+1,j})\\ f_{i,j}=dp_{i+1,j}+f_{i+1,j} \end{cases} \]

当然,并不是所有情况都能合并,所以需要维护 \(p_{i,j,k}\)\(q_{i,j,k}\),表示 \(k\) 能否接在 \(i\)\(j\) 的左侧/包住 \(i\)\(j\)

时间、空间复杂度均为 \(O(n^3)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=305,md=998244353;
int t,n,m,f[N][N],dp[N][N];
int p[N][N][N],q[N][N][N];
void solve(){
	memset(dp,0,sizeof(dp));
	memset(f,0,sizeof(f));
	memset(p,0,sizeof(p));
	memset(q,0,sizeof(q));
	cin>>n>>m;
	int flag=0;
	while(m--){
		int x,y;cin>>x>>y;
		if(x==y||flag){
			flag=1;
			continue;
		}if(x<y) q[y][y][x]=1;
		else p[x][x][y]=1;
	}if(flag){
		cout<<"0\n";
		return;
	}for(int i=1;i<=n;i++)
		f[i][i]=1;
	for(int ln=2;ln<=n;ln++)
		for(int i=1,j=ln;j<=n;i++,j++)
			for(int k=1;k<=n;k++){
				p[i][j][k]|=p[i+1][j][k]|p[i][i][k];
				q[i][j][k]|=q[i+1][j][k]|q[i][i][k];
			}
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)
			for(int k=1;k<=n;k++)
				p[i][j][k]+=p[i][j][k-1];
	for(int ln=2;ln<=n;ln++)
		for(int i=1,j=ln;j<=n;i++,j++){
			for(int k=i;k<j;k++){
				if(p[k+1][j][k]-p[k+1][j][i-1]) continue;
				if(p[i][k][j]-p[i][k][k]) continue;
				dp[i][j]=(dp[i][j]+(ll)f[i][k]*(dp[k+1][j]+f[k+1][j])%md)%md;
			}if(!q[i+1][j][i]) f[i][j]=(f[i+1][j]+dp[i+1][j])%md;
		}
	cout<<(dp[1][n]+f[1][n])%md<<"\n";
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--) solve();
	return 0;
} 
posted @ 2024-07-08 10:12  长安一片月_22  阅读(8)  评论(0编辑  收藏  举报