[CF1781F] Bracket Insertion

发布时间 2023-11-05 22:37:33作者: StranGePants

Bracket Insertion
托利斯特老了/kk
我一开始想的是 () 的权值为1,)( 的权值为-1,树上 dp 满足某子树由若干条类似斯特兰数方案的链构成。
但是有概率使得权值计算很难处理。
所以考虑把合法方案最终状态表示出来。
套路地设 ( 的权值为1,) 的权值为-1。
可以发现若新加入 (),其插入位置之前的前缀和为 x,之后的前缀和为 y,则 (x,y) 变为 (x,x+1,x,y)。
若加入 )( 则变为 (x,x-1,x,y)。
那么我们转化题目为,初始集合为{0},每次选择一个数进行上面的操作之1,n 次操作中途无负数的方案数。
\(dp_{i,j}\) 表示初始集合为 {j},操作 i 次后合法的方案数。
那么

\[dp_{i,j}=P\sum_{x=0}^{n-1}\sum_{y=0}^{n-x-1}\binom{n-1}{x}\binom{n-x-1}{y}dp_{x,j+1}dp_{y,j}dp_{n-1-x-y,j}\\ +(1-P)\sum_{x=0}^{n-1}\sum_{y=0}^{n-x-1}\binom{n-1}{x}\binom{n-x-1}{y}dp_{x,j}dp_{y,j-1}dp_{n-1-x-y,j} \]

这个式子表示完成第一次操作后,把 x 次操作分给第一个 x 和其之后的操作,y 次操作分给 x+/-1 和其之后的操作,剩下的操作分给第二个 x。
发现有重复的部分,提一下就能 \(\Omicron(n^3)\) 了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<bitset>
using namespace std;
#define db double
#define ll long long
#define ull unsigned long long
#define ld long double
#define b1t bitset
const int MAXN=500+5;
const int Mod=998244353;
const int bas=10000;
ll n,p,c[MAXN][MAXN],f[MAXN][MAXN],g[MAXN][MAXN],Ans;
void read(ll &x){
	x=0;ll f=1;char s=getchar();
	while(s<'0'||s>'9'){
		if(s=='-') f=-1;s=getchar();
	}
	while(s>='0'&&s<='9'){
		x=(x<<3ll)+(x<<1ll)+(s^48);s=getchar();
	}
	x*=f;
}
ll ksm(ll a,int b){
	ll res=1;
	while(b){
		if(b&1) res=res*a%Mod;
		a=a*a%Mod;b>>=1;
	}return res;
}
int main(){
	read(n);read(p);p=p*ksm(bas,Mod-2)%Mod;
	c[0][0]=1;
	for(int i=1;i<=n;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%Mod;
	}
	for(int i=0;i<=n;i++) f[0][i]=g[0][i]=1;
	for(int i=1;i<=n;i++){
		for(int x=0;x<=n;x++){
			for(int j=0;j<i;j++){
				f[i][x]=(f[i][x]+(p*f[j][x+1]+(Mod+1-p)*(x>0?f[j][x-1]:0))%Mod*c[i-1][j]%Mod*g[i-j-1][x]%Mod)%Mod;
			}
			for(int j=0;j<=i;j++){
				g[i][x]=(g[i][x]+c[i][j]*f[j][x]%Mod*f[i-j][x]%Mod)%Mod;
			}
		}
	}
	Ans=f[n][0];
	for(int i=1;i<=(n<<1);i+=2) Ans=Ans*ksm(i,Mod-2)%Mod;
	printf("%lld",Ans); 
	return 0;
}