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