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();
    }
}

  

posted on 2011-09-21 19:43  Fatedayt  阅读(410)  评论(0编辑  收藏  举报

导航