Luogu P3830 [SHOI2012]随机树 | 期望 DP
第$1$问
设$g(i)$为当有$i$个叶节点时,树的叶节点的平均深度的期望值。
假如现在有$(i-1)$个叶节点,现在要等概率地展开一个叶节点,那么展开的那个叶节点的期望深度为$g(i-1)$,展开后那两个新的叶节点的期望深度均为$(g(i-1)+1)$,叶节点深度总和的期望值增加了$(g(i-1)+2)$。
所以,$g(i)=\dfrac{(i-1)g(i-1)+g(i-1)+2}{i}=g(i-1)+\dfrac{2}{i}$
边界:$g(1)=0$
第$2$问
设$f(i,j)$为当有$i$个叶节点时,树的深度大于等于$j$的概率。
假设现在有$i$个叶节点,树的深度大于等于$j$。
考虑枚举根节点左子树中叶节点的个数,假设有$k$个,则根节点右子树中叶节点的个数为$(i-k)$个。
现在要计算生成这样一棵树的概率是多少。
我们不妨先计算生成这样一棵树有多少种方案。
对于第一次展开,一定是展开根节点。
第一次展开后,左子树中还要展开$(k-1)$次,右子树中还要展开$(i-k-1)$次。
显然,从一个节点开始,展开$x$次的方案数有$x!$种。
那么,左子树展开的方案数即有$(k-1)!$种,右子树展开的方案数即有$(i-k-1)!$种。
对于每一次展开,我们既可以展开左子树中的叶节点,也可以展开右子树中的叶节点。
综上,生成一棵这样的树的方案数为
$(k-1)!(i-k-1)!C_{k-1+i-k-1}^{k-1}=(k-1)!(i-k-1)!\dfrac{(i-2)!}{(k-1)!(i-k-1)!}=(i-2)!$
所以,生成一棵根节点的左子树中有$k$个叶节点,右子树中有$(i-k)$个叶节点的树的方案数为$(i-2)!$,与$k$无关。
因为$1\leqslant k \leqslant i-1$且$k$为整数,且对于每一个$k$,方案数均为$(i-2)!$。
所以,生成一棵根节点的左子树中有$k$个叶节点,右子树中有$(i-k)$个叶节点的树的概率为$\dfrac{1}{i-1}$。
接着我们就来考虑,对于一棵根节点的左子树中有$k$个叶节点,右子树中有$(i-k)$个叶节点的树,树的深度大于等于$j$的概率是多少。
显然为$f(k,j-1)+f(i-k,j-1)-f(k,j-1)\cdot f(i-k,j-1)$
所以$f(i,j)=\sum\limits_{k=1}^{i-1}(f(k,j-1)+f(i-k,j-1)-f(k,j-1)\cdot f(i-k,j-1))\cdot\dfrac{1}{i-1}$
边界:$f(i,0)=1(1\leqslant i \leqslant n)$
$ans=\sum\limits_{i=1}^{n-1}i(f(n,i)-f(n,i+1))$
#include<iostream> #include<cstdio> using namespace std; double g[105],f[105][105]; int main() { int q=0,n=0; scanf("%d%d",&q,&n); if(q==1) { g[1]=0; for(int i=2;i<=n;i++) g[i]=g[i-1]+(double)2/i; printf("%.6f",g[n]); } if(q==2) { for(int i=1;i<=n;i++) f[i][0]=1; for(int i=2;i<=n;i++) for(int j=1;j<=i-1;j++) for(int k=1;k<=i-1;k++) f[i][j]+=(f[k][j-1]+f[i-k][j-1]-f[k][j-1]*f[i-k][j-1])/(i-1); double ans=0; for(int i=1;i<=n-1;i++) ans+=i*(f[n][i]-f[n][i+1]); printf("%.6f",ans); } return 0; }