【BZOJ3244】树的计数(NOI2013)-概率期望+数学证明

测试地址:树的计数
做法:本题需要用到概率期望+数学证明。
要求树的期望高度,我们知道树的BFS的层数就是它的高度,所以我们要对BFS序分层。但由于有DFS序的限制,我们需要更加深入地考虑DFS序对BFS序的限制。为了方便,我们把BFS序映射成1,..,n,并对应地修改DFS序。
首先,显然BFS序的一种划分方案要么不合法,要么和一种满足要求的树一一对应。
然后,由于题目中的条件:一个点的儿子在DFS序中出现的顺序和在BFS中出现的顺序相同,所以BFS序中同一层的点出现的顺序应该和DFS序中出现的顺序相同。我们把这称为条件一。
接下来,DFS序中相邻的两个点u,vv要么是u的某个祖先的儿子,要么是u的儿子,那么我们有depth(u)+1depth(v)。我们把这称为条件二。
那么我们考虑对BFS序分层,实际上就是考虑在哪些间隙添加断点,那么期望层数就是期望断点数+1。首先在1后面是肯定要分层的,然后因为条件一的限制,如果两个在BFS序中相邻的点u,v中,u在DFS序中的出现位置比v的出现位置靠后,那么这两个点就不能被分在同一层中,它们中间必须有一个断点。再考虑条件二,我们可以把不等式写成depth(v)depth(u)1,也就是说,如果u在BFS序中在v之前,那么它们之间至多只能有一个断点,否则它们的深度差就会超过1。我们用已经确定必须要断的点来更新这种情况,即如果一个限制区间中已经有断点,那么限制区间中的其它点就不能断,这个可以用类似差分的标记方法解决。而对于没有断点的限制区间,可以证明这样的区间包含的间隙数目只可能为1(证明见下面的补证),那么这个点有12的概率是断点。根据期望的线性性,我们直接把每个间隙是断点的概率相加,就是期望断点数,再加上1就是期望层数,也就是树的期望高度了,时间复杂度为O(n)
补一个小证明:不包含断点的限制区间包含的间隙数只能是1。使用反证法,假设它包含的间隙数大于1,因为区间内不包含由条件一得到的断点,所以区间内的点在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;
}
posted @ 2018-05-15 16:22  Maxwei_wzj  阅读(101)  评论(0编辑  收藏  举报