把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

题解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);

完结撒花!!!

posted @ 2023-04-25 14:30  djh0314  阅读(20)  评论(0)    收藏  举报  来源
浏览器标题切换
浏览器标题切换end