XXVII.[SHOI2012]随机树
\(q=1\):
考虑令\(f_i\)表示:一棵有\(i\)个叶节点的树,叶节点平均深度的期望值。
则\(f_i=f_{i-1}+\dfrac{2}{i}\)。
证明:
我们随便从\(i-1\)个叶子中选一个出来,展开它,
则这次展开期望能为叶子的深度和增加\(2*(f_{i-1}+1)-f_{i-1}\)。
但是还要重新取平均;
于是
\(f_i=\dfrac{f_{i-1}*(i-1)+2*(f_{i-1}+1)-f_{i-1}}{i}\)
化简就得到了
\(f_i=f_{i-1}+\dfrac{2}{i}\)
\(q=2\):
考虑令\(f[i][j]\)表示:
一棵有\(i\)个叶节点的树,深度\(\geq j\)的概率。
显然,有\(f[i][j]=\sum\limits_{k=1}^{i-1}\dfrac{f[k][j-1]+f[i-k][j-1]-f[k][j-1]*f[i-k][j-1]}{?}\)
释义:我们枚举左子树中放进去\(k\)个子节点。则左/右节点至少有一个深度\(\geq j-1\)的状态都是合法的。但是,左右节点深度都\(\geq j-1\)的情况在两个中都被算进去了;因此需要减去这种可能。
这个分母上的\(?\),就是出现这种左右子树\(size\)分配的可能性。
考虑这种可能性的大小。
我们设\(g_i\)表示构成一棵有\(i\)个叶节点的树的方案数。则有\(g_i=(i-1)!\),因为每“扩展”一个点就相当于从\(i\)个叶子中选了一个叶子出来,有\(i\)种选法;则有\(g_i=\prod\limits_{j=1}^{i-1}j=(i-1)!\)。
显然,这种可能性应该等于\(g_{k}*g_{i-k}*?\)(这个\(?\)是一个新的\(?\))。我们将两棵子树合并,首先两棵子树自己内部扩展的顺序已经被\(g\)决定了;但是合并的顺序可是可以随便指定的;合并顺序的种数等于\(C_{i-2}^{k-1}\),因为\(i\)个叶节点就意味着\(i-2\)次合并(当前节点自己本身就占用一次合并),这\(i-2\)次合并选出\(k-1\)次合并在左子树上。
则可能性的大小为\(g_{k}*g_{i-k}*C_{i-2}^{k-1}=(k-1)!*(i-k-1)!*\dfrac{(i-2)!}{(k-1)!*(i-2-k+1)!}=(i-2)!\)
然后分母上的\(?\)就是\(\dfrac{g_i}{(i-2)!}=\dfrac{(i-1)!}{(i-2)!}=(i-1)\)。
于是我们现在有
\(f[i][j]=\sum\limits_{k=1}^{i-1}\dfrac{f[k][j-1]+f[i-k][j-1]-f[k][j-1]*f[i-k][j-1]}{i-1}\)
有一个式子:
\(E(x)=\sum\limits_{i=1}^{\infty}P(i)\)
其中\(E(x)\)表示\(x\)的期望,\(P(i)\)表示\(i\leq x\)的概率。
证明:
设\(p(i)\)表示\(i=x\)的概率,\(P(i)\)表示\(i\leq x\)的概率,
则\(E(x)=\sum\limits_{i=1}^{\infty}p(i)*i\)
而\(P(i)=\sum\limits_{j=i}^{\infty}p(j)\)
则
\(\begin{aligned}\sum\limits_{i=1}^{\infty}P(i)&=\sum\limits_{i=1}^{\infty}\sum\limits_{j=i}^{\infty}p(j)\\&=\sum\limits_{i=1}^{\infty}p(i)*i\\&=E(x)\end{aligned}\)
证毕。
依据此式,则答案为\(\sum\limits_{i=1}^{n-1}f[n][i]\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
namespace T1{
double f[110];
double work(){
f[1]=0;
for(int i=2;i<=n;i++)f[i]=f[i-1]+2.0/i;
return f[n];
}
}
namespace T2{
double f[110][110];
double work(){
for(int i=1;i<=n;i++)f[i][0]=1;
for(int i=2;i<=n;i++)for(int j=1;j<i;j++){
for(int k=1;k<i;k++)f[i][j]+=f[i-k][j-1]+f[k][j-1]-f[i-k][j-1]*f[k][j-1];
f[i][j]/=i-1;
}
double res=0;
for(int i=1;i<n;i++)res+=f[n][i];
return res;
}
}
int main(){
scanf("%d%d",&m,&n);
if(m==1)printf("%lf\n",T1::work());
if(m==2)printf("%lf\n",T2::work());
return 0;
}