【BZOJ3244】树的计数(NOI2013)-概率期望+数学证明
测试地址:树的计数
做法:本题需要用到概率期望+数学证明。
要求树的期望高度,我们知道树的BFS的层数就是它的高度,所以我们要对BFS序分层。但由于有DFS序的限制,我们需要更加深入地考虑DFS序对BFS序的限制。为了方便,我们把BFS序映射成,并对应地修改DFS序。
首先,显然BFS序的一种划分方案要么不合法,要么和一种满足要求的树一一对应。
然后,由于题目中的条件:一个点的儿子在DFS序中出现的顺序和在BFS中出现的顺序相同,所以BFS序中同一层的点出现的顺序应该和DFS序中出现的顺序相同。我们把这称为条件一。
接下来,DFS序中相邻的两个点,要么是的某个祖先的儿子,要么是的儿子,那么我们有。我们把这称为条件二。
那么我们考虑对BFS序分层,实际上就是考虑在哪些间隙添加断点,那么期望层数就是期望断点数。首先在后面是肯定要分层的,然后因为条件一的限制,如果两个在BFS序中相邻的点中,在DFS序中的出现位置比的出现位置靠后,那么这两个点就不能被分在同一层中,它们中间必须有一个断点。再考虑条件二,我们可以把不等式写成,也就是说,如果在BFS序中在之前,那么它们之间至多只能有一个断点,否则它们的深度差就会超过。我们用已经确定必须要断的点来更新这种情况,即如果一个限制区间中已经有断点,那么限制区间中的其它点就不能断,这个可以用类似差分的标记方法解决。而对于没有断点的限制区间,可以证明这样的区间包含的间隙数目只可能为(证明见下面的补证),那么这个点有的概率是断点。根据期望的线性性,我们直接把每个间隙是断点的概率相加,就是期望断点数,再加上就是期望层数,也就是树的期望高度了,时间复杂度为。
补一个小证明:不包含断点的限制区间包含的间隙数只能是。使用反证法,假设它包含的间隙数大于,因为区间内不包含由条件一得到的断点,所以区间内的点在DFS序中出现的顺序应该和在BFS序中出现的顺序相同,但是一个限制区间是由DFS序中相邻两个点确定的,这样就无论如何都不能使得这些点在DFS序中和在BFS序中出现的顺序相同,矛盾,所以待证结论必然成立。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,dfs[200010],p[200010];
int pos[200010],type[200010]={0},sum[200010],tag[200010]={0};
double ans=1.0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&dfs[i]);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
p[x]=i;
}
for(int i=1;i<=n;i++)
{
dfs[i]=p[dfs[i]];
pos[dfs[i]]=i;
}
sum[0]=0;
for(int i=1;i<n;i++)
{
if (i==1||pos[i]>pos[i+1])
type[i]=1;
sum[i]=sum[i-1]+type[i];
}
for(int i=1;i<n;i++)
if (dfs[i]<dfs[i+1]&&sum[dfs[i+1]-1]-sum[dfs[i]-1]>0)
{
tag[dfs[i]]++;
tag[dfs[i+1]]--;
}
int now=0;
for(int i=1;i<n;i++)
{
now+=tag[i];
if (!type[i]&&now) type[i]=2;
}
for(int i=1;i<n;i++)
{
if (type[i]==1) ans+=1.0;
if (!type[i]) ans+=0.5;
}
printf("%.3lf",ans);
return 0;
}