P3830 [SHOI2012]随机树

斯德哥尔摩

2018-10-06 00:38:25

Personal

[P3830 [SHOI2012]随机树](https://www.luogu.org/problemnew/show/P3830) 第一问很好处理: 设$f_x$表示有$x$个叶子节点的树的叶子节点平均深度。 我们考虑在一个有$x-1$个叶子节点的树里随机选择一个叶子节点展开,那么树的叶子节点深度总和会增加$f_{x-1}+2$。 所以:$$f_x=\frac {f_{x-1}\times(x-1)+f_{x-1}+2}{x}=f_{x-1}+\frac{2}{x}$$ 第二问就比较难受了: 首先我们知道一个式子: $$E(x)=\sum_{i=1}^{+}P(i\leq x)$$ 说人话就是随机变量$x$的期望为对于所有$i$,$i\leq x$的概率之和。 我们设$dp[i][j]$表示有$i$个叶子,树的深度$\geq j$的概率。 转移时枚举左右子树有多少个叶子: $$dp[i][j]=\sum_{k=1}^{i-1}\frac{1}{i-1}(dp[k][j-1]+dp[i-k][j-1]-dp[k][j-1]\times dp[i-k][j-1])$$ 括号里的式子含义: 左右只要一边深度$\geq j-1$即可,所以式子展开其实是$dp[k][j-1]\times 1+dp[i-k][j-1]\times 1$,但这样会计算两次两边都$\geq j-1$的情况,所以需要减掉。 算是一个容斥吧。 最后答案即为:$$\sum_{i=1}^{n-1}dp[n][i]$$ 附代码: ```cpp #include<iostream> #include<algorithm> #include<cstdio> #define MAXN 110 using namespace std; int id,n; double f[MAXN],dp[MAXN][MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void work_one(){ f[1]=0; for(int i=2;i<=n;i++)f[i]=f[i-1]+2.00/i; printf("%.6lf\n",f[n]); } void work_two(){ double ans=0; for(int i=1;i<=n;i++)dp[i][0]=1; for(int i=2;i<=n;i++) for(int j=1;j<i;j++){ for(int k=1;k<i;k++)dp[i][j]+=dp[k][j-1]+dp[i-k][j-1]-dp[k][j-1]*dp[i-k][j-1]; dp[i][j]/=1.00*(i-1); } for(int i=1;i<n;i++)ans+=dp[n][i]; printf("%.6lf\n",ans); } void init(){ id=read();n=read(); } int main(){ init(); if(id==1)work_one(); else work_two(); return 0; } ```