AGC060C Large Heap 题解 / 计数 dp
题目传送门:AGC060C Large Heap。
首先这个东西完全就是一个满二叉树的堆,相当于权值顺序要为一个拓扑,且 \(U\) 在 \(V\) 前面。
而 \(U,V\) 在 \(A+1\) 与 \(B+1\) 层的最左和左右端点。
那么 \(U,V\) 分别是两条最左或最右的链。
而拓扑序的开头一定为 \(1\),分成两棵树,然后再找一个根再分裂,那么我们只需要考虑存在 \(U,V\) 的树,剩下的随便填即可,那么最主要的就是存在 \(U,V\) 的两棵树的深度。
考虑 dp,设 \(s_i\) 表示深度为 \(i\) 满二叉树拓扑方案数,\(g_{i,j}\) 表示两棵树的深度分别为 \(i,j\) 的方案数,\(f_{i,j}\) 表示概率。
转移的话考虑选择左右那个树。
\[s_i = s_{i-1}^2 C_{2^i-2}^{2^{i-1}-1}
\]
\[g_{i,j}=g_{i-1,j} s_{i-1} C_{2^i+2^j-3}^{2^{i-1}-1} + g_{i,j-1}s_{j-1}C_{2^i+2^j-3}^{2^j-1}
\]
\[f_{i,j}=\frac{g_{i,j}}{s_is_jC_{2^i+2^j-2}^{2^i-1}}
\]
将 \(g_{i,j}\) 代入 \(f_{i,j}\) 并用 \(f\) 表示 \(g\) 化简可得
\[f_{i,j}=f_{i-1,j}\frac{2^i-1}{2^i+2^j-2} + f_{i,j-1}\frac{2^j-1}{2^i+2^j-2}
\]
显然初始时 \(f_{n-A-1,j}=1\) 其中 \(j\ge n-B\)。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
const int N=5010,mod=998244353;
int f[N][N],A,B,n,g[N];
void exgcd(int a,int b,int &x,int &y){
if (b==0) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
inline int inv(int a){
int x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
inline void add(int &x,int y){x=(x+y)%mod;}
main(){
n=read(),A=read(),B=read();
g[0]=1;for (int i=1;i<=n;i++) g[i]=g[i-1]*2%mod;
for (int i=n-B;i<=n;i++) f[n-A-1][i]=1;
for (int i=n-A;i<=n;i++) for (int j=n-B;j<=n;j++)
add(f[i][j],f[i-1][j]*(g[i]-1+mod)%mod*inv(g[i]+g[j]-2+mod)%mod+f[i][j-1]*(g[j]-1+mod)%mod*inv(g[i]+g[j]-2+mod)%mod);
cout <<f[n-1][n-1];
return 0;
}

浙公网安备 33010602011771号