vijos1984—— 随机树

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();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值