【[SHOI2012]随机树】
感觉第一问就非常神仙,还有第二问怎么被我当成组合数学题来做了
首先是第一问
期望具有线性性,于是深度平均值的期望等于深度和的期望值的平均
设\(dp_x\)表示具有\(x\)个叶子节点的树的深度和的期望值是多少
我们发现扩展一个叶子节点的实质将其变成两个深度原来大一的叶节点,所以对整个答案的贡献也就是这个被扩展的叶子节点的深度乘\(2\),再加上\(2\)
比如我们当前扩展的是叶子节点\(1\),那么答案就从\(dep_1+dep_2+...+dep_x\)变成了\(2*(dep_1+1)+dep_2+...+dep_x\)
\(dep_2,dep_3\)同理,也就会发现在最终的答案里每个\(dep\)都出现了\(x+1\)次
设\(dp[x]\)表示\(x\)个叶子节点的期望深度和,\(dp[x]=\sum_{i=1}^xdep_x\)
那么期望是
\]
\]
也就是
\]
最后的答案就是\(\frac{dp[n]}{n}\)了
之后第二问我就感受到了玄学的力量,各种玄学调参数
第二问好像非常麻烦的样子,没有办法像刚才那个样子从平均的角度来考虑了,而直接求期望好像不太好求,于是可以求出概率来
设\(dp[x][h]\)表示有\(x\)个叶子节点构成的树深度为\(h\)的概率是多少,那么答案就是\(\sum_{h=1}^ndp[n][h]*h\)
我们考虑一下如何求这个\(dp[n][h]\)
有一个比较套路的东西就是枚举左右子树有几个叶子节点
所以就有
\]
其中\(p[i][j]=\sum_{k=1}^jdp[i][k]\)也就是一个概率的前缀和,\(P_{i,k}\)表示一共\(i\)个叶子节点其中\(k\)个在左子树上的概率
也就是枚举左右子树的叶子节点的个数,之后对应好相应的深度,乘上这个转移发生的概率
先不考虑这个\(P_{i,k}\)怎么求,也会发现上面那个转移好像有些问题,它算重了左右两边子树的深度都是\(j-1\)的情况,于是上面还需要再减掉\(dp[k][j-1]*dp[i-k][j-1]\)
现在的问题就变成了\(P_{i,k}\)怎么求了
首先经过感性理解/手玩样例/归纳证明可以发现,在不同的扩展顺序下使得左子树上有\(k\)个叶子节点的概率是一个固定的值
我们要让左右两边共有\(i\)个叶子节点,也就是说我们一共需要扩展\(i-1\)次,第一次扩展肯定是需要扩展在当前的这个节点上的,于是还要有\(i-2\)次扩展被分给了左右子树
我们再来考虑一下使得左子树上有\(k\)个叶子节点的实质是什么,不就是分给左边的扩展次数为\(k-1\)吗,那么这样一共有\(\binom{i-2}{k-1}\)种扩展情况会使得左子树上扩展了\(k-1\)次
又因为这些不同的扩展顺序出现的概率是一样的,所以我们可以考虑一些求出这个概率
这个概率的分母上肯定是\(2*3*4*5*...*(i-1)\),因为一共需要扩展产生\(i\)个节点每次选中左子树或者右子树的概率是\(\frac{\text{左/右子树上叶子节点数量}}{\text{叶子节点的总数量}}\),而分子上由于我们在左边一共选择了\(k\)次,所以分母会有\(1*2*..*(k-1)\),也就会有相应的\(1*2*...*(i-k-1)\)
所以
\]
我们再顺便化一下柿子
\]
我下面的代码预处理了阶乘和组合数,其实直接用\(\frac{1}{i-1}\)就好了
我才不会说我看到题解才想起来继续化柿子的
于是这样转移就好了
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define re register
#define maxn 105
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
int opt_Q;
int n;
namespace Ask1
{
double dp[maxn];
inline void init()
{
n=read();
dp[1]=0;
for(re int i=1;i<n;i++)
dp[i+1]=((i+1)*dp[i]+2*i)/double(i);
printf("%.6lf",dp[n]/double(n));
}
}
namespace Ask2
{
double dp[maxn][maxn+100],pre[maxn][maxn];
long double fac[maxn];
long double c[maxn][maxn];
inline void init()
{
n=read();
dp[1][0]=1;dp[2][1]=1;dp[3][2]=1;
for(re int i=0;i<=n;i++) pre[1][i]=1;
for(re int i=1;i<=n;i++) pre[2][i]=1;
for(re int i=2;i<=n;i++) pre[3][i]=1;
fac[0]=1;
for(re int i=1;i<=n;i++) fac[i]=fac[i-1]*i;
c[0][0]=1;
for(re int i=1;i<=n;i++) c[i][0]=c[i][i]=1;
for(re int i=2;i<=n;i++)
for(re int j=1;j<n;j++)
c[i][j]=c[i-1][j-1]+c[i-1][j];
for(re int i=4;i<=n;i++)
for(re int j=log2(i);j<=n;j++)
{
for(re int k=1;k<i;k++)
dp[i][j]+=(dp[k][j-1]*pre[i-k][j-1]+dp[i-k][j-1]*pre[k][j-1]-dp[k][j-1]*dp[i-k][j-1])*c[i-2][k-1]*fac[k-1]*fac[i-k-1]/fac[i-1];
pre[i][j]=dp[i][j]+pre[i][j-1];
}
double ans=0;
for(re int h=log2(n);h<=n;h++)
ans+=dp[n][h]*h;
printf("%.6lf",ans);
}
}
int main()
{
opt_Q=read();
if(opt_Q==1) Ask1::init();
else Ask2::init();
return 0;
}
【[SHOI2012]随机树】的更多相关文章
- [SHOI2012]随机树
[SHOI2012]随机树 题目大意( 网址戳我! ) 随机树是一颗完全二叉树,初始状态下只有一个节点. 随机树的生成如下:每次随机选择一个叶子节点,扩展出两个儿子. 现在给定一个正整数\(n\)(\ ...
- P3830 [SHOI2012]随机树 题解
P3830 随机树 坑题,别人的题解我看了一个下午没一个看得懂的,我还是太弱了. 题目链接 P3830 [SHOI2012]随机树 题目描述 输入输出格式 输入格式: 输入仅有一行,包含两个正整数 q ...
- P3830 [SHOI2012]随机树
P3830 [SHOI2012]随机树 链接 分析: 第一问:f[i]表示有i个叶子结点的时候的平均深度,$f[i] = \frac{f[i - 1] + 2 + f[i - 1] * (i - 1) ...
- bzoj2830: [Shoi2012]随机树
题目链接 bzoj2830: [Shoi2012]随机树 题解 q1好做 设f[n]为扩展n次后的平均深度 那么\(f[n] = \frac{f[n - 1] * (n - 1) + f[n - 1] ...
- luogu P3830 [SHOI2012]随机树 期望 dp
LINK:随机树 非常经典的期望dp. 考虑第一问:设f[i]表示前i个叶子节点的期望平均深度. 因为期望具有线性性 所以可以由每个叶子节点的期望平均深度得到总体的. \(f[i]=(f[i-1]\c ...
- luogu3830 [SHOI2012]随机树
传送门:洛谷 题目大意:对于一个只有一个节点的二叉树,一次操作随机将这棵树的叶节点的下方增加两个节点.$n-1$次操作后变为$n$个叶节点的二叉树.求:(1)叶节点平均深度的期望值(2)树深度的数学期 ...
- luogu P3830 [SHOI2012]随机树
输入格式 输入仅有一行,包含两个正整数 q, n,分别表示问题编号以及叶结点的个数. 输出格式 输出仅有一行,包含一个实数 d,四舍五入精确到小数点后 6 位.如果 q = 1,则 d 表示叶结点平均 ...
- [SHOI2012]随机树[期望dp]
题意 初始 \(1\) 个节点,每次选定一个叶子节点并加入两个儿子直到叶子总数为 \(n\),问叶子节点深度和的平均值的期望以及最大叶子深度的期望. \(n\leq 100\) . 分析 对于第一问, ...
- 洛谷P3830 [SHOI2012]随机树(期望dp)
题面 luogu 题解 第一问: 设\(f[i]\)表示\(i\)步操作后,平均深度期望 \(f[i] = \frac {f[i - 1] * (i - 1)+f[i-1]+2}{i}=f[i-1]+ ...
随机推荐
- Grid控件
Grid控件是WPF布局容器中功能最强大.最灵活的控件.Grid控件基本上能够完成其他WPF容器控件所能完成的功能,Microsoft建议大多数界面的布局都使用Grid控件来实现,因此默认情况下.vs ...
- Axios的详细配置和相关使用
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. Features 从浏览器中创建 XMLHttpRequests 从 node.js 创建 http ...
- Vue 多路由文件的合并
Vue 多路由文件的合并 1.使用的是ES6 数组的合并方法 let routes = new Set([...routes1, ...homerouters]);2.两个路由文件,导出的实际上就是一 ...
- Hadoop学习笔记(10) ——搭建源码学习环境
Hadoop学习笔记(10) ——搭建源码学习环境 上一章中,我们对整个hadoop的目录及源码目录有了一个初步的了解,接下来计划深入学习一下这头神象作品了.但是看代码用什么,难不成gedit?,单步 ...
- HBase入门教程
# 背景 最近看到公司一个项目用到hbase, 之前也一直想看下hbase.个人理解Hbase作为一个nosql数据库,逻辑模型感觉跟关系型数据库有点类似.一个table,有row即行,列.不过列是一 ...
- .NET 垃圾回收浅解
在说明垃圾回收的实现机制之前,先说明一下垃圾回收存在的背景. 垃圾回收器(GC)是.NET平台中一个很重要的组成部分,.NET垃圾回收机制降低了编写程序的复杂程度,使程序员不用耗费精力去处理析构,成功 ...
- MySQL图形化界面SQLyog
软件地址:SQLyog 密码: mmaf
- Java设计模式—享元模式
享元模式:是池技术的重要实现方式. 定义如下: 使用共享对象可有效地支持大量的细粒度的对象. 个人理解:享元模式利用共享对象的技术,解决了Java中内存溢出的问题. 享元模式的定义为我们提出了两个要求 ...
- 由Dialog里面嵌套ListView之后的高度自适应引起的ListView性能优化
先说ListView给高的正确做法. android:layout_height属性: 必须将ListView的布局高度属性设置为非"wrap_content"(可以是" ...
- .PHONY makefile中的伪目标
我的理解: 拿clean举例,如果make完成后,自己另外定义一个名叫clean的文件,再执行make clean时,将不会执行rm命令. 为了避免出现这个问题,需要.PHONY: clean === ...