• P3830 SHOI2012]随机树


    链接 P3830 [SHOI2012]随机树

    • 这题好难啊……

    • 首先第一问:

    • (f_i)为有(i)个叶子节点的树的平均深度,(d_i)为任意树的任意一点深度

    • 则有(f_i*i)为有(i)个叶子节点的树的深度和

    • 随机一次操作所能对平均深度和的贡献((d_i+1)*2-d_i)

    • 可得:(f_i=f_{i-1}+frac{2}{i})

    • 可以看成是新增加了(2)的深度贡献分配在现在的(i)个点上。

    • 然后第二问:

    • (g_{i,j})表示在有(i)个叶子的随机树中,树的深度等于(j)的概率。

    • 答案为(sum g_{n,i}*i)

    • 然而(题解说他不好转移)我们发现他不好转移……。

    • 改成(f_{i,j})表示在有(i)个叶子的随机树中,树的深度大于等于(j)的概率(猫贼讲过的套路)。

    • 显然,对于某一棵随机的,左儿子有(k)个叶子,右儿子有(i−k)个叶子的树,它的深度不小于(j)的概率为

    [G_k=f_{k,j-1}+f_{i-k,j-1}-f_{k,j-1}*f_{i-k,j-1} ]

    • 这也就是说,事件(a,b)发生的概率(=)事件(a)发生的概率加上事件(b)发生的概率,再减去两个事件同时发生的概率。
    • 然后题解说(f_{i,j}=sum frac {G_k}{i-1})…………然而没有证明啊。
    • 证明一下:
    • 设生成一棵的左儿子恰有(k)个叶子,右儿子有(i-k)个叶子的树的概率为(P_k)
    • 那么我们只要证明对于任何(P_k),均有$$P_k=frac {1}{i-1}$$
    • 考虑一棵左儿子有(k)个叶子,右儿子有(i−k)个叶子的树是怎样生成的?
    • 相当于你有长度为(i)的序列,每次选择改变左儿子或者右儿子,那么方案数为:
    • [C_{i-2}^{k-1} ]

    • 因为第一次一定同时增加了左右子树一个叶子,所以不是(C_{i}^{k})
    • 注意操作和操作之间是有区别的,所以操作的方案数乘阶乘,为((i-2)!)
    • 注意这是一个和(k)没有关系的量,即任意(P_k)均相等,所以得证。
    • 初始条件(f_{i,0}=1),表示一定存在深度。
    • 注意到答案显然不是(sum f_{n,i}*i)了,考虑怎么算答案。
    • 发现(f)实际上就是(g)的前缀和,即(f_{n,i}=sum_{k=i}^{n}g_{n,k})
    • 考虑把这个东西写成一个矩阵,(f)的每一项是横着算得,而答案是竖着算得,所以答案就是
    • [sum f_{n,i} ]

    #include<bits/stdc++.h>
    #define R register int
    #define ll long long
    #define db double 
    using namespace std;
    const int N=200;
    int q,n;db g[N],f[N][N],ans;
    void sol1(){
    	for(R i=2;i<=n;++i)g[i]=g[i-1]+2.0/(db)i;
    	printf("%.6lf
    ",g[n]);
    }
    void sol2(){
    	for(R i=1;i<=n;++i)f[i][0]=1;
    	for(R i=2;i<=n;++i)
    		for(R j=1;j<i;++j){
    			for(R 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]/=(db)(i-1);
    		}
    //	for(R i=1;i<n;++i)printf("f[%d][%d]=%.5lf
    ",n,i,f[n][i]);
    	for(R i=1;i<n;++i)ans+=f[n][i];
    	printf("%.6lf
    ",ans);
    }
    int main(){
    	freopen("s.in","r",stdin);
    	cin>>q>>n;if(q==1)sol1();else sol2();
    	return 0;
    }
    
    
  • 相关阅读:
    《C++ Primer》之面向对象编程(四)
    《C++ Primer》之面向对象编程(三)
    《C++ Primer》之面向对象编程(二)
    《C++ Primer》之面向对象编程(一)
    《C++ Primer》之指向函数的指针
    C++ 隐式类类型转换和转换操作符
    python中的sum函数.sum(axis=1)
    numpy中tile函数
    sscanf函数详解
    snprintf()返回值的陷阱
  • 原文地址:https://www.cnblogs.com/Tyher/p/9840205.html
Copyright © 2020-2023  润新知