HDU4008 Parent and son

  HDU4008 Parent and son

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)
Total Submission(s): 1035    Accepted Submission(s): 249


Problem Description
Give you a tree with N vertices and N‐ 1 edges, and then ask you Q queries on “which vertex is Y's son that has the smallest number and which vertex is Y’s descendants that has the smallest number if we choose X as the root of the entire tree?”
 

Input
The first line of input is an integer T (T<=10) means the case number. 
The first line of each test case contains N(2 ≤ N ≤ 100,000) and Q(1 ≤ Q ≤ 100,000). 
Each of the following N ‐ 1 lines of the test case contains two integers a(1 ≤ a ≤ N) and b(1 ≤ b ≤ N) indicating an edge between a and b. 
Each of the following Q lines of the test case contains two integers X(1 ≤ X ≤ N) and Y(1 ≤ Y ≤ N, Y ≠ X) indicating an query. 
 

Output
For each query, output the Y's son which has the smallest number and Y's descendant that has the smallest number if X is the root of the entire tree. If Y has no sons then output “no answers!”. There is an empty line after each case.
 

Sample Input
1 7 3 1 2 1 5 2 3 2 4 5 6 5 7 1 2 5 3 3 2
 

Sample Output
3 3 no answers! 1 1
 

Source
 
**************************************************************
题目大意:给你一棵树,这个树的根节点不定。然后有Q次询问。对于每次询问,给定x和y。即当整棵树以x为根的时候,求y的所有儿子中最小的那个编号值以及y的所有
子孙中最小的编号值。
解题思路: http://blog.csdn.net/hqd_acm/article/details/6750163我主要是参考了一篇文章,思路是他的,实现是我的。
还有不得不说的神乎其神的dfn时间戳数组。初识dfn数组是在前几天的图的连通性问题中见识到,当时觉得一个时间戳竟然如此好用。今天更感其神啊。当dfs一棵树的时候,
我们可以递归得到一个节点他是所有子孙中的最大的时间戳,把这个也记录下来。然后奇迹啊!我们可以在o(1)的时间内判断y是不是在x的子树下面。
其他的看那篇博客就好了。
本人代码附上:
#include <stdio.h>

#include <string.h>

#include <vector>

#include <algorithm>

#define MIN(a,b) ((a)<(b)?(a):(b))

#define MAX(a,b) ((a)>(b)?(a):(b))

#define INF 0x3f3f3f3f

#define N 100005

using namespace std;



vector<int>gra[N],ht;

int mindown[N],fa[N],minson[N][2],dfn[N],now,maxdfn[N];

int n,m;

/*mindown数组用来储存每个节点以下最小的子孙的值,fa数组储存每个节点的

父亲值,minson的两个值分别是每个节点下的最小和次小的儿子值,dfn神乎其神

的时间戳数组,now用来盖印时间戳,maxdfn数组用来储存每个节点子孙中的最大

的dfn值*/



void ini(void)//初始化

{

    memset(fa,0,sizeof(fa));

    memset(dfn,0,sizeof(dfn));

    memset(maxdfn,0,sizeof(maxdfn));

    now=0;

    for(int i=1;i<=n;i++)

        gra[i].clear();

}



void dfs(int s,int f)//预处理

{

    fa[s]=f;

    dfn[s]=maxdfn[s]=++now;

    ht.clear();

    mindown[s]=minson[s][0]=minson[s][1]=INF;

    for(int i=0;i<gra[s].size();i++)

        if(!fa[gra[s][i]])

            ht.push_back(gra[s][i]);

    gra[s]=ht;

    for(int i=0;i<gra[s].size();i++)

    {

        int t=gra[s][i];

        dfs(t,s);

        mindown[s]=MIN(mindown[s],t);

        mindown[s]=MIN(mindown[s],mindown[t]);

        if(t<minson[s][0])

            minson[s][1]=minson[s][0],minson[s][0]=t;

        else if(t<minson[s][1])

            minson[s][1]=t;

        maxdfn[s]=MAX(maxdfn[s],maxdfn[t]);

    }

}



int erfen(int x,int y)//二分查找儿子

{

    int le=0,ri=gra[y].size(),mid;

    while(1)

    {

        mid=(le+ri)>>1;

        if(dfn[x]>=dfn[gra[y][mid]]&&dfn[x]<=maxdfn[gra[y][mid]])return gra[y][mid];

        if(dfn[x]>maxdfn[gra[y][mid]])le=mid+1;

        if(dfn[x]<dfn[gra[y][mid]])ri=mid-1;

    }

}



void re(void)//输入

{

    scanf("%d%d",&n,&m);

    ini();

    for(int i=1;i<n;i++)

    {

        int a,b;

        scanf("%d%d",&a,&b);

        gra[a].push_back(b);

        gra[b].push_back(a);

    }

}



void run(void)//运行

{

    dfs(1,-1);

    int temp[2]={INF,INF};//temp表示1节点儿子中mindown的最小值和次小值

    for(int i=0;i<gra[1].size();i++)

    {

        int t=gra[1][i];

        if(mindown[t]<temp[0])

            temp[1]=temp[0],temp[0]=mindown[t];

        else if(mindown[t]<temp[1])

            temp[1]=mindown[t];

    }

    for(int h=1;h<=m;h++)//m次询问

    {

        int x,y;

        scanf("%d%d",&x,&y);

        int fx=-1,fy=-1;

        if(dfn[x]>dfn[y]&&dfn[x]<=maxdfn[y])//在o(1)的时间内知道x是不是y的子孙

            fx=erfen(x,y);

        if(dfn[y]>dfn[x]&&dfn[y]<=maxdfn[x])

            fy=erfen(y,x);

        if(fx==-1)//要么y是x的子孙,要么y和x不在一棵子树上面

        {

            if(mindown[y]==INF)printf("no answers!\n");

            else printf("%d %d\n",minson[y][0],mindown[y]);

        }

        else//x是y的子孙,当y是否等于1的时候有特殊处理

        {

            if(y!=1)

            {

                if(fx!=minson[y][0])

                    printf("%d %d\n",MIN(minson[y][0],fa[y]),1);

                else

                {

                    if(gra[y].size()==1)

                        printf("%d %d\n",fa[y],1);

                    else

                        printf("%d %d\n",MIN(minson[y][1],fa[y]),1);

                }

            }

            else

            {

                if(gra[y].size()==1)

                    printf("no answers!\n");

                else

                {

                    int a=temp[0];

                    if(a!=INF&&dfn[a]>=dfn[fx]&&dfn[a]<=maxdfn[fx])

                            a=temp[1];

                    int b=minson[1][0];

                    if(fx==minson[1][0])

                        b=minson[1][1];

                    printf("%d %d\n",b,MIN(a,b));

                }

            }

        }

    }

    puts("");

}



int main()

{

    int ncase;

    scanf("%d",&ncase);

    while(ncase--)

    {

        re();

        run();

    }

}

  

你可能感兴趣的:(HDU)