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();
}