祖孙询问 (lca或dfs序+时间戳)

问题 F: 祖孙询问

时间限制: 1 Sec  内存限制: 128 MB
提交: 13  解决: 6  难度:提高+/省选-
[提交] [状态] [讨论版] [命题人:xcgzjia]

题目描述

输入

输出

样例输入 Copy

10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19

样例输出 Copy

1
0
0
0
2

提示

 

 
 思路:
  方法1
  很明显是一道求lca的题
  求出lca判断就可以了
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int mssc=4e4+5;
 4 int n,m;
 5 int d[mssc],f[mssc][20];
 6 vector<int> g[mssc];
 7 void pre(int u,int fa){//初始化 
 8     d[u]=d[fa]+1;//求深度 
 9     for(int i=1;i<=15;i++){//i的范围根据题的数据范围而定 
10         f[u][i]=f[f[u][i-1]][i-1];
11     }
12     int sz=g[u].size();//表示u所连接的边数 
13     for(int i=0;i<sz;i++){
14         if(g[u][i]!=fa){//g[u][i]表示以u为起点所连接的第i条边的终点 
15             f[g[u][i]][0]=u;
16             pre(g[u][i],u);
17         }
18     }
19 }
20 int lca(int x,int y){
21     if(d[x]<d[y]){//让x为深度最深的点 
22         swap(x,y);
23     }
24     for(int i=15;i>=0;i--){//跳到同一深度 
25         if(d[f[x][i]]>=d[y]){
26             x=f[x][i];
27         }
28         if(x==y){//如果此时是同一个点就输出 
29             return x;
30         }
31     }
32     for(int i=15;i>=0;i--){//让x,y同时向上跳 
33         if(d[f[x][i]]!=d[f[y][i]]){//如果不是同一点就更新x,y的值 
34             x=f[x][i];             //如果是同一点那么x的父节点就是答案 
35             y=f[y][i];
36         }
37     }
38     return f[x][0];//答案就是x的父节点 
39 }
40 int main(){
41     scanf("%d",&n);
42     int root;
43     int a,b;
44     for(int i=1;i<=n;i++){
45         scanf("%d%d",&a,&b);
46         if(b==-1){
47             root=a;
48         }else{
49             g[a].push_back(b);
50             g[b].push_back(a);    
51         }
52     }
53     pre(root,0);
54     scanf("%d",&m);
55     for(int i=1;i<=m;i++){
56         scanf("%d%d",&a,&b);
57          int t=lca(a,b);
58         if(t==a&&b!=t){
59             printf("%d
",1);        
60         }else if(t==b&&a!=t){
61             printf("%d
",2);
62         }else{
63             printf("%d
",0);
64         }
65     }
66     return 0;
67 }
 方法2
  求出给定树的dfs序
  观察可以发现,如果存在x,y有祖孙关系,那么无非就有两种情况:
  第一种是x在y的区间内
  第二种就是y在x的区间内
  那么我们只要求出x和y进入dfs的时间和离开的时间查看他们的包含情况就可以了
  也就是说 求出这棵树所有点dfs序的时间戳 问询的时候判断一下即可
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=400005;
 4 int len,n,m,tim;
 5 int st[maxn],et[maxn];
 6 int pos[maxn];
 7 vector<int> g[maxn];
 8 void dfs(int u,int fa){
 9     st[++len]=++tim;
10     pos[u]=len;
11     int sz=g[u].size();
12     for(int i=0;i<sz;i++){
13         if(g[u][i]!=fa){
14             dfs(g[u][i],u);
15         }
16     }
17     et[pos[u]]=++tim;
18 }
19 int main(){
20     scanf("%d",&n);
21     int a,b,root;
22     for(int i=1;i<=n;i++){
23         scanf("%d%d",&a,&b);
24         if(b==-1){
25             root=a;
26         }else{
27             g[a].push_back(b);
28             g[b].push_back(a);
29         }
30     }
31     dfs(root,0);
32     scanf("%d",&m);
33     for(int i=1;i<=m;i++){
34         scanf("%d%d",&a,&b);
35         a=pos[a];
36         b=pos[b];
37         if(st[a]<st[b]&&st[b]<et[b]&&et[b]<et[a]){//a is elder
38             printf("%d
",1);
39         }else if(st[a]>st[b]&&st[a]<et[a]&&et[b]>et[a]){//b is elder
40             printf("%d
",2);
41         }else{
42             printf("%d
",0);
43         }
44     }
45     return 0;
46 }
 
原文地址:https://www.cnblogs.com/duojiaming/p/11298876.html