[SHOI2012]随机树

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;
}

posted @ 2021-03-30 15:27  Troverld  阅读(92)  评论(0编辑  收藏  举报