BZOJ3244[NOI2013树的计数]

Description:

      给定一棵 N(N<=200000)个节点的树的 DFSBFS`序,求所有满足要求的树的平均深度。
      
      
      

Solution:

      考虑到 BFS序的性质,BFS在前的点的深度一定小于等于后面的点。所以我们考虑根据 BFS序计算答案。
      首先根据 BFS序给树上的点重编号,按 BFS序的先后编成 1,2,...,N,考虑相邻的点 i对答案的贡献,如果它和 i1必须在不同的层,那么对答案的贡献为 1,如果它和 i1必须在同一层,那么对答案的贡献为 0,否则为 0.5,最后只需要把所有的贡献加起来就行了 (1,21)。
      必须不在同一层很好判断,考虑一下必须在同一层的情况,如果点i在 DFS序中的位置在点 i1前面的话,那么就一定在不同层。
      否则只剩下在必须在同一层或者都可以。
      在同一层和不同层都可以的情况,显然需要满足 DFS序中 i1,i两个点必须是连续的。我们画图发现,如果在一棵按 BFS序编号的树中,点 i可以变作 i1的儿子的并且不改变 DFS,BFS序的话,只能是 i变换后, i所在的那一层只有 i一个点并且 i,i1应该有共同的父亲,那么这个条件等价于 1i1号点在 DFS序中 1l的一段和 rN的一段,也就是说在 DFS序中必须是前面一段最后一段。然后其他情况就是必须在同一层了。
      最后记住 BZOJ上不知道为什么需要输出三行,分别为Ans0.001,Ans,Ans+0.001
      
      
      

Code:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>

using namespace std;

int N;
int dfs[200010]={0};
int bfs[200010]={0};
int In[200010]={0};
int Max[200010]={0};
int hash[200010]={0};

int main()
{
    cin>>N;
    for(int i=1;i<=N;i++)
    {
        scanf("%d",&dfs[i]);
        In[dfs[i]]=i;
    }
    for(int i=1;i<=N;i++)
    {
        scanf("%d",&bfs[i]);
        dfs[In[bfs[i]]]=i;
        bfs[i]=i;
    }
    for(int i=1;i<=N;i++)
        In[dfs[i]]=i;
    Max[1]=dfs[1];
    for(int i=2;i<=N;i++)
        Max[i]=max(Max[i-1],dfs[i]);
    double Ans=2;
    int l=2,r=N+1;
    hash[1]=hash[2]=1;
    for(int i=3;i<=N;i++)
    {
        if(In[i]<In[i-1])
            Ans+=1;
        else if(In[i]==In[i-1]+1)
        {
            if(l+N+1-r==i-1)
                Ans+=0.5;
        }
        hash[In[i]]=1;
        for(;l<r && hash[l+1]==1;l++);
        for(;l<r && hash[r-1]==1;r--);
    }
    printf("%.3lf\n",Ans);
    //In bzoj printf("%.3lf\n%.3lf\n%.3lf\n",Ans-0.001,Ans,Ans+0.001);
    return 0;
}

版权声明:本文为qq_21995319原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。