hdu 4008 Parent and son

33 篇文章 0 订阅
20 篇文章 0 订阅

Parent and son


树形DP题目。题目意思很简单,给定一个无根树,然后给定Q个查询x , y 查询以x为该无根树的根节点,然后查询y的最小儿子,以及中最小子孙。

思路:首先我们规定树的根为1,然后进行一次dfs,求解出每个节点x的儿子节点中最小的和次小的,子孙节点中最小的,同时求出x的所有子节点保存的最小子孙节点中的次小子孙节点,以及每个节点x的父亲节点。数组含义如下:pre[x] : x的父亲节点,low[x][0]:x的最小子孙节点,low[x][1],x所有子树中的最小子孙节点中的次小子孙节点,son[x][0]:x的最小儿子节点,son[x][1]:x的次小儿子节点。

对与查询x , y有如下情况:

1.如果x节点不是y的子孙节点,那么很明显当树进行旋转的时候并不会影响y节点的值,此时问题的答案就是son[y][0] , low[y][0]

2.如果x节点为y的子孙节点,很明显树进行旋转操作的时候,y的相关值也发生了变化。此时我们先对y的最小儿子节点进行计算。

首先:如果设z为x的某个祖先节点,同时有z的父亲节点为y,那么显然经过旋转z变成了y的父亲节点,如果z是节点y保存的最小儿子节点,那么此时y的最小儿子节点肯定在y的次小儿子节点与y的父节点中。如果y是1,那么此时答案就是y的次小儿子节点。如果y不是1,那么此时答案很明显就是min(son[y][1] , pre[y]).如果z不是节点y保存的最小儿子节点,那么此时y的最小儿子节点肯定在y的最小儿子与y的父节点之间。如果y是1,那么就是y的最小儿子,否则min(son[y][0] , pre[y]).

其实求解y的最小子孙节点也是相同方法:首先如果y是1,low[z][0]==low[y][0] or low[y][0] == z, 那么结果肯定就为low[y][1],若low[z][0] != low[y][0] and low[y][0]!=z ,结果肯定是low[y][0] .否则如果y不是1,那么1肯定是最小的子孙节点。

#include <cstdio>
#include <cstring>
#include <vector>

#define maxn 100005
#define INF 0x3f3f3f3f
std::vector<int> G[maxn] ;

int son[maxn][2] ;//son[i][0]表示i的儿子节点中最小的,son[i][1]儿子节点中次小的
int low[maxn][2] ;//low[i][0]表示i的后继节点中最小的,low[i][1]后继节点中次小的(不同的树上)
int pre[maxn]    ;//父亲节点
int N ;
int Q ;

void init(){
    for(int i = 0 ; i <= N ; i ++){
        pre[i] = i ;
        G[i].clear() ;
    }
    memset(son , 0x3f , sizeof(son[0]) * (N + 2)) ;
    memset(low , 0x3f , sizeof(low[0]) * (N + 2)) ;
}

void swap(int &x , int &y){
    x = x ^ y ;
    y = x ^ y ;
    x = x ^ y ;
}

void dfs(int v , int f){
    pre[v] = f ;
    for(int i = 0 ; i < G[v].size() ; i ++){
        int u = G[v][i] ;
        if(u!=f){
            dfs(u , v) ;
            //son
            if(son[v][1] > u)
                son[v][1] = u ;
            if(son[v][0] > son[v][1])
                swap(son[v][0] , son[v][1]) ;
            //des
            int nmin = low[u][0] < u ? low[u][0] : u ;
            if(low[v][1] > nmin)
                low[v][1] = nmin ;
            if(low[v][0] > low[v][1])
                swap(low[v][1] , low[v][0]) ;
        }
    }
}

int find(int x , int y){
    while(pre[x] != x){
        if(pre[x] == y)
            return x ;
        x = pre[x] ;
    }
    return 0 ;
}

int main(){
    int Case ;
    scanf("%d" , &Case) ;
    for(int t = 1 ; t <= Case ; t ++){
        int x ;
        int y ;

        scanf("%d%d" , &N , &Q) ;
        init() ;
        for(int i = 1 ; i < N ; i ++){
            scanf("%d%d" , &x , &y) ;
            G[x].push_back(y) ;
            G[y].push_back(x) ;
        }
        dfs(1 , 1) ;

        for(int i = 1 ; i <= Q ; i ++){
            scanf("%d%d" , &x , &y) ;
            int tmp = find(x , y) ;
            int ans1 = INF ;
            int ans2 = INF ;
            if(tmp == 0){
                ans1 = son[y][0] ;
                ans2 = low[y][0] ;
            }
            else{
                 if(y != 1){
                    if(tmp==son[y][0]){
                        ans1 = pre[y] < son[y][1] ? pre[y] : son[y][1] ;
                    }
                    else{
                        ans1 = pre[y] < son[y][0] ? pre[y] : son[y][0] ;
                    }
                    ans2 = 1 ;
                 }
                 else{
                    if(tmp == son[y][0])
                        ans1 = son[y][1] ;
                    else
                        ans1 = son[y][0] ;
                    if(low[tmp][0] == low[y][0] || low[y][0] == tmp)
                        ans2 = low[y][1] ;
                    else
                        ans2 = low[y][0] ;
                 }
            }
            if(ans1 == INF || ans2 == INF)
                printf("no answers!\n") ;
            else
                printf("%d %d\n" , ans1 , ans2) ;
        }
        printf("\n") ;
    }
    return 0 ;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值