vijos1984—— 随机树

来源:互联网 发布:python学习教程 编辑:程序博客网 时间:2024/05/29 17:16

vijos1984
本题好像是SHOI d1T3?
反正我抢下了vijos上这题的一血啊哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

这里写图片描述

代码其实很短,1K不到

对于第一个问题,很显然的瞎几把dp一下就可以了

if(k==1)    {        f[1]=0;        For(i,2,n)            f[i]=f[i-1]+f[i-1]/(i-1)+2;         printf("%.6f",f[n]/n);    }

感觉也没什么可以说的。。。
重点是第二问!
妈的想了好久好久,问了敦敦敦才做出来的
我们令f[i][j]表示有i个叶子节点,层数为j的概率

那么这个状态的转移,可以理解为:
有一棵 x个叶子节点的树,深度为j-1,以及另外一棵i-x个叶子节点的树,深度为y
以这两棵树作为左右子树,加上一个根节点,合并,就可以得到一棵i个叶子节点层数为j的树
当然。。由于扩展的顺序可以不同,所以实际的方案数为:(x-1)!*(i-x-1)!*C(x-1,i-2)
然而要求概率,我们要除以总方案:(i-2)!
所以。。(x-1)!*(i-x-1)!*C(x-1,i-2)/(i-2)!=1;
好,那么这个影响我们就可以无视掉了

所以我们只要枚举x和y就可以完成f[i][j]的转移
也就是O(n^4)
然而n<=100,显然可以过

这里要注意一下!如果y取到y-1,我们只能算一次(其他的都要*2,因为左右可以调换)
因为f[x][j-1]和f[i-x][j-1]本身会被取到两次,如果还乘2,,相当于去到4次,因此要特别容斥一下

CODE:

#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>#include<queue>#include<string>#include<cstring>#define inf 1e9#define ll long long#define For(i,j,k) for(int i=j;i<=k;i++)#define Dow(i,j,k) for(int i=k;i>=j;i--)using namespace std;double f[101],f2[101][101],ans;int n,k;inline void doit(){    f2[1][0]=1;    f2[2][1]=1;    For(j,2,n)        For(i,3,n)        {            For(k,0,j-1)                For(t,1,i-1)                    f2[i][j]+=f2[t][j-1]*f2[i-t][k]/(i-1)*2;            For(ii,1,i-1)   f2[i][j]-=f2[ii][j-1]*f2[i-ii][j-1]/(i-1);        }    For(i,1,n)        ans+=f2[n][i]*i;    printf("%.6f",ans);}int main(){    scanf("%d%d",&k,&n);    if(k==1)    {        f[1]=0;        For(i,2,n)            f[i]=f[i-1]+f[i-1]/(i-1)+2;         printf("%.6f",f[n]/n);    }    if(k==2)        doit();}
0 0