hdu 4008 Parent and son 在树上查询以X为根的Y的儿子和子树节点中的最小节点 标记时间戳

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

//


#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=(1<<30);
const int maxn=220000;
struct PK
{
    int u;
    int dfn;
};
vector<PK> sot[maxn];
struct Node
{
    int t;
    int next;
};
int n;
Node G[maxn];
int p[maxn];
int l;
void init()
{
    memset(p,-1,sizeof(p));
    l=0;
}
void addedge(int u,int t)
{
    G[l].t=t;
    G[l].next=p[u];
    p[u]=l++;
}
struct Pass
{
    int st,ed;
};
Pass pas[maxn];
int dfn;
Node g[maxn];
int d[maxn];
int k;
void iinit()
{
    memset(d,-1,sizeof(d));
    k=0;
}
void iaddedge(int u,int t)
{
    g[k].t=t;
    g[k].next=d[u];
    d[u]=k++;
}
struct TMin
{
    int min1,min2;
};
TMin tmin[maxn];
int fa[maxn];
void solve(int u,int t)
{
    if(t>=tmin[u].min2);
    else if(t>=tmin[u].min1)
    {
        tmin[u].min2=t;
    }
    else
    {
        tmin[u].min2=tmin[u].min1;
        tmin[u].min1=t;
    }
}


void dfs(int u,int fath)
{
    sot[u].clear();
    fa[u]=fath;
    pas[u].st=++dfn;
    for(int i=p[u];i!=-1;i=G[i].next)
    {
        int t=G[i].t;
        if(t==fath) continue;
        solve(u,t);
        iaddedge(u,t);
        dfs(t,u);
        PK tmp;tmp.u=t;tmp.dfn=pas[t].ed;
        sot[u].push_back(tmp);
    }
    pas[u].ed=++dfn;
}
bool cmp(PK h,PK k)
{
    return h.dfn<k.dfn;
}




struct node
{
    int left,right,maxn,minc;
};
node tree[maxn*3];//构造线段树
int _max,_min;//当前最大值和最小者,查询是用
void buildtree(int id,int l,int r)//建树
{
    tree[id].left=l;tree[id].right=r;//初始化左右端点
    tree[id].maxn=-inf;tree[id].minc=inf;//初始化最大,小值
    if(l!=r)//是根节点
    {
        buildtree(2*id,l,(l+r)/2);//左子树
        buildtree(2*id+1,(l+r)/2+1,r);//右子树
    }
}
void insert(int id,int i,int val)
{
    if(tree[id].left==i&&tree[id].right==i)//叶子节点
    {
        tree[id].minc=tree[id].maxn=val;
        return ;
    }
    tree[id].maxn=max(tree[id].maxn,val);//更新次节点的最大值
    tree[id].minc=min(tree[id].minc,val);//更新次节点的最小值
    if(i<=(tree[id].left+tree[id].right)/2)  insert(2*id,i,val);//进去左子树
    else insert(2*id+1,i,val);//进去右子树
}
void query(int id,int l,int r)//查询
{
    if(l>r) return ;
    if(tree[id].left==l&&tree[id].right==r)//叶子节点
    {
        _min=min(tree[id].minc,_min);
        _max=max(tree[id].maxn,_max);
        return ;
    }
    int mid=(tree[id].left+tree[id].right)/2;
    if(r<=mid)//左子树
    {
        query(2*id,l,r);
    }
    else if(l>=mid+1)//右子树
    {
        query(2*id+1,l,r);
    }
    else//横跨左右子树
    {
        query(2*id,l,mid);//左
        query(2*id+1,mid+1,r);//右
    }
}






int main()
{
    int ci;scanf("%d",&ci);
    while(ci--)
    {
        int q;
        scanf("%d%d",&n,&q);
        init();
        iinit();
        for(int i=1;i<=n;i++) tmin[i].min1=tmin[i].min2=inf;
        for(int i=0;i<n-1;i++)
        {
            int u,t;scanf("%d%d",&u,&t);
            addedge(u,t);
            addedge(t,u);
        }
        dfn=0;
        dfs(1,inf);
        for(int i=1;i<=n;i++)
        {
            sort(sot[i].begin(),sot[i].end(),cmp);
        }
        buildtree(1,1,n+n);
        for(int i=1;i<=n;i++)
        {
            insert(1,pas[i].st,i);
            insert(1,pas[i].ed,i);
        }
        while(q--)
        {
            int x,y;scanf("%d%d",&x,&y);//x!=y
            if(pas[y].st<pas[x].st&&pas[y].ed>pas[x].ed)//x是y的子树
            {
                int ans=inf;
                int first;
                int t;
                int l=0,r=sot[y].size();
                while(l<r)
                {
                    int mid=(l+r)/2;
                    if(sot[y][mid].dfn>=pas[x].ed) r=mid;
                    else l=mid+1;
                }
                t=sot[y][l].u;
                //cout<<"t="<<t<<endl;
                _max=-inf,_min=inf;//初始化
                query(1,1,pas[y].st-1);
                ans=min(ans,_min);
                _max=-inf,_min=inf;//初始化
                query(1,pas[y].st+1,pas[t].st-1);
                ans=min(ans,_min);
                _max=-inf,_min=inf;//初始化
                query(1,pas[t].ed+1,pas[y].ed-1);
                ans=min(ans,_min);
                _max=-inf,_min=inf;//初始化
                query(1,pas[y].ed+1,n+n);
                ans=min(_min,ans);
                if(tmin[y].min1==t)
                {
                    first=min(tmin[y].min2,fa[y]);
                }
                else
                {
                    first=min(tmin[y].min1,fa[y]);
                }
                if(ans!=inf) printf("%d %d\n",first,ans);
                else printf("no answers!\n");
            }
            else //y是x的子树
            {
                _max=-inf,_min=inf;//初始化
                query(1,pas[y].st+1,pas[y].ed-1);
                if(_min!=inf) printf("%d %d\n",tmin[y].min1,_min);
                else printf("no answers!\n");
            }
        }
        printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值