[CF1781F] Bracket Insertion
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;
}
本文来自博客园,作者:{StranGePants},转载请注明原文链接:https://www.cnblogs.com/StranGePants/p/17811345.html