题解P3830 [SHOI2012]随机树
传送门
题意
非常显然。
Question 1
部分分($n\le10$)
对于每次长叶子时,就是把一个叶子深度 +1 ,并且复制。
对此可以写出 $dfs$ 暴力 20 分。
整分
可以从上面看出,每次分裂一个叶子,总价值可以增加 (原叶子深度+2)。
由此,令 $f[i]$ 为 $i$ 个叶子时的叶子深度平均值的期望价值。
则 $f[i]\times i=f[i-1]\times (i-1)+f[i-1]+2$ 。
整理可得 $f[i]=f[i-1]+\frac{2}{i}$。
时间复杂度 $O(n)$ 。
Question 2
部分分($n\le 10$)
与上面的dfs大致相同,由于对正解没用,就不赘述。
正解
方法一 $O(n^{4})$
对于一棵深度为 $deep$ 的二叉树,
我们可以将其根节点的两棵子树分开,
就会分成一棵深度为 $deep-1$ 的二叉树,与一棵深度为 $q(1\le q \le j-1)$ 的二叉树。
顺着这个思路,我们可以将两个二叉树组合,以变成深度更大的一棵树。
若一棵二叉树深度为 $j$,另一棵深度为 $q$,
那么两者组合起来的二叉树深度为 $\max \left ( j,q\right ) +1$ 。
且组合起来的树的叶子数量必然大于两棵子树的叶子数。
由此,令 $f[i][j]$ 表示 生成一棵叶子结点为 $i$ 个,深度为 $j$ 的二叉树的概率。
可以写出递推式: $f[i][j] = f[k][q] \times f[i-k][j-1]$ 。
于是我们可以愉快的 A 掉这题了。
f[1][0]=1;
for(int i=2; i<=n; ++i) {
for(int j=1; j<=i; ++j) {
for(int k=1; k<i; ++k) {
for(int q=0; q<j-1; ++q)
f[i][j]+=f[k][q]*f[i-k][j-1]*2;
f[i][j]+=f[k][j-1]*f[i-k][j-1];
}
f[i][j]/=(i-1);
}
}
db ans=0;
for(int i=1; i<=n; ++i) ans+=f[n][i]*i;
printf("%.6f",ans);
但是我们可以观察到,可以对此代码进行优化。
方法二 $O(n^{3})$
在循环 $q$ 时,我们进行了计算了 $ {\textstyle \sum_{q=0}^{j-2}} {}f[k][q]\times f[i-k][j-1]\times 2$ 。
明显的,我们可以对于这一句进行前缀和优化,即可优化至 $O(n^3)$。
同样附上代码:
f[1][0]=1;
qzh[1][0]=1;
for(int i=1; i<=n; ++i) qzh[1][i]=qzh[1][i-1]+f[1][i];
for(int i=2; i<=n; ++i) {
for(int j=1; j<=i; ++j) {
for(int k=1; k<i; ++k) {
if(j>=2) f[i][j]+=qzh[k][j-2]*f[i-k][j-1]*2;
f[i][j]+=f[k][j-1]*f[i-k][j-1];
}
f[i][j]/=(i-1);
}
qzh[i][0]=f[i][0];
for(int j=1; j<=n; ++j) qzh[i][j]=qzh[i][j-1]+f[i][j];
}
db ans=0;
for(int i=1; i<=n; ++i) ans+=f[n][i]*i;
printf("%.6f",ans);
此刻你觉得这道题完了吗,事情并没有如此完结,此题还有一种方法。
方法三 $O(n^3)$
我们稍微改变一下 $f[i][j]$ 的定义,将其定义为叶子数为 $i$,深度大于等于$j$ 的概率。
由此 $f[i][j]=f[k][j-1]+f[i-k][j-1]-f[k][j-1]* f[i-k][j-1]$ ,
稍微解释一下,要么是左子树大于等于 $j-1$,要么是右边,且要减去都大于等于 $j-1$ 的概率。
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[k][j-1]+f[i-k][j-1]-f[k][j-1]*f[i-k][j-1];
f[i][j]/=(i-1);
}
}
db ans=0;
for(int i=1; i<=n; ++i) ans+=f[n][i];
printf("%.6f",ans);

浙公网安备 33010602011771号