过之前觉得这题紫题实在是评分过低,过了之后才发现这题紫题是评分过高????
首先第1问就开始玄学:
设
f
x
f_x
fx为x个叶子的期望平均深度,那么你拓展一个叶子的期望增加总深度为
f
x
+
2
f_x+2
fx+2。
f
x
+
1
=
x
f
x
+
f
x
+
2
x
+
1
=
f
x
+
2
x
f_{x+1} = \frac {xf_x+f_x+2}{x+1}=f_x + \frac 2x
fx+1=x+1xfx+fx+2=fx+x2
好巧(气)啊。。。。。。
如果不能接受可以
O
(
n
2
)
d
p
O(n^2)dp
O(n2)dp出有一个深度恰好为d的叶子的概率再乘上d算个总和就行。。。
第二问:
发现这个不好dp,开始拆:
首先是期望拆成概率:
E
(
a
n
s
)
=
∑
i
=
1
∞
i
P
(
i
=
x
)
=
∑
i
=
1
∞
i
(
P
(
x
>
=
i
)
−
P
(
x
>
=
i
+
1
)
)
=
∑
i
=
1
∞
P
(
x
>
=
i
)
E(ans) = \sum_{i=1}^{\infty}iP(i=x)\\=\sum_{i=1}^{\infty}i(P(x>=i)-P(x>=i+1))\\=\sum_{i=1}^{\infty}P(x>=i)
E(ans)=i=1∑∞iP(i=x)=i=1∑∞i(P(x>=i)−P(x>=i+1))=i=1∑∞P(x>=i)
然后发现深度大于i的概率还是不好求。
继续拆:
我们发现随机树的生成方式对计数很不友好,把他拆出来。
(莫名其妙)发现树的左右子树内操作次数固定的情况下(条件概率)深度大于等于一个数的概率,和生成方式生成满足这树左右操作次数的概率是独立的。
那就考虑两个怎么分别求。
考虑二叉树式dp:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为i次操作的子树深度大于等于j。
自然想到枚举左子树的操作次数。
设左子树操作次数为k,
那么(在左子树操作次数为k的情况下)深度大于等于j的概率为:
d
p
[
k
]
[
j
−
1
]
+
d
p
[
i
−
k
−
1
]
[
j
−
1
]
−
d
p
[
k
]
[
j
−
1
]
∗
d
p
[
i
−
k
−
1
]
[
j
−
1
]
dp[k][j-1] + dp[i-k-1][j-1] - dp[k][j-1] *dp[i-k-1][j-1]
dp[k][j−1]+dp[i−k−1][j−1]−dp[k][j−1]∗dp[i−k−1][j−1]
然后再看左子树操作次数为k,右子树操作次数为i-1-k的概率。
发现这个有点像插空法,考虑用方案数/总方案数。
方案数为:
左子树确定一个顺序:
k
!
k!
k!
右子树确定一个顺序:
(
i
−
k
−
1
)
!
(i-k-1)!
(i−k−1)!
左右子树相间方案:在
i
−
1
i-1
i−1个盒子中选
k
k
k个作为左子树,
(
k
i
−
1
)
\binom{k}{i-1}
(i−1k)。
三个相乘发现是
(
i
−
1
)
!
(i-1)!
(i−1)!
再除总方案数
i
!
i!
i!为
1
i
\frac 1i
i1
然后就完美解决了???
AC Code:
#include<bits/stdc++.h>
#define maxn 105
using namespace std;
int p,n;
double dp[maxn][maxn];
int main()
{
scanf("%d%d",&p,&n);
if(p == 1)
{
double ans = 0;
for(int i=2;i<=n;i++)
ans = ans + 2.0 / i;
printf("%.6lf",ans);
}
else
{
dp[0][0] = 1;
for(int i=1;i<n;i++)
{
for(int dep=1;dep<=n;dep++)
{
for(int j=0;j<i;j++)
{
dp[i][dep] += dp[j][dep-1] + dp[i-1-j][dep-1] - dp[j][dep-1] * dp[i-1-j][dep-1];
}
dp[i][dep] /= i;
}
dp[i][0] = 1;
}
double ans = 0;
for(int i=1;i<=n;i++)
ans += dp[n-1][i];
printf("%.6lf",ans);
}
}