P3379 【模板】最近公共祖先(LCA)

题目链接:https://www.luogu.com.cn/problem/P3379

方法一:暴力求公共祖先

1.向上标记法(重点了解26-44行)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, m, s;
 4 const int maxn=5e6;
 5 const int maxm=5e6;
 6 struct edge{
 7     int t, nex;
 8 }e[maxm<<1];
 9 int head[maxn], cnt;
10 void add(int u, int v){
11     e[++cnt].t=v;
12     e[cnt].nex=head[u];
13     head[u]=cnt;
14 }
15 int depth[maxn], fa[maxn];
16 void dfs(int now, int fath)
17 {
18     fa[now]=fath; //fa[i]代表i的父亲 
19     depth[now]=depth[fath]+1;
20     //cout<<now<<":"<<fa[now]<<" "<<depth[now]<<endl; 
21     for(int i=head[now]; i; i=e[i].nex)
22         if(e[i].t != fath)
23             dfs(e[i].t, now);
24 }
25 
26 int flag[maxn]; //向上标记法
27 int lca1(int x, int y) 
28 {
29     memset(flag, 0, sizeof(flag));//每次初始化为0 
30     if(x==y)return x;
31     flag[x]=1;
32     while(fa[x]){//x向上走到根,根的父亲节点为0 
33         x=fa[x];
34         flag[x]=1; 
35     }
36     if(flag[y])//y节点是x的祖先 
37         return y;
38     while(fa[y]){
39         y=fa[y];
40         if(flag[y])
41             return y;
42     }
43     return 0;//森林,无LCA 
44 } 
45 int main()
46 {
47     scanf("%d%d%d", &n, &m, &s);
48     for(int i=1; i<n; i++){
49         int x, y;
50         scanf("%d%d", &x, &y);
51         add(x, y); add(y, x);
52     }
53     dfs(s, 0);
54     while(m--){
55         int a, b;
56         scanf("%d%d", &a, &b);
57         printf("%d\n", lca1(a, b));
58     }
59     return 0;
60 }
View Code

2.同步前进法(重点了解25-38行)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n, m, s;
 4 const int maxn=5e6;
 5 const int maxm=5e6;
 6 struct edge{
 7     int t, nex;
 8 }e[maxm<<1];
 9 int head[maxn], cnt;
10 void add(int u, int v){
11     e[++cnt].t=v;
12     e[cnt].nex=head[u];
13     head[u]=cnt;
14 }
15 int depth[maxn], fa[maxn];
16 void dfs(int now, int fath)
17 {
18     fa[now]=fath; //fa[i]代表i的父亲 
19     depth[now]=depth[fath]+1;// 
20     //cout<<now<<":"<<fa[now]<<" "<<depth[now]<<endl; 
21     for(int i=head[now]; i; i=e[i].nex)
22         if(e[i].t != fath)
23             dfs(e[i].t, now);
24 }
25 int lca(int x, int y) //同步前进法
26 {
27     if(x==y)return x;//同点 
28     if(fa[x]==fa[y])return fa[x];//同根
29     if(depth[x]<depth[y])//不妨设x的深度 >= y的深度
30         swap(x, y);
31     while(depth[x] > depth[y])
32         x=fa[x];
33     if(x==y)return x;//y节点是x的祖先
34     while(fa[x] != fa[y]){//父亲不相等,两个点同时网上爬,直到父亲相等 
35         x=fa[x]; y=fa[y];
36     }
37     return fa[x];//返回父亲节点 
38 } 
39 int main()
40 {
41     scanf("%d%d%d", &n, &m, &s);
42     for(int i=1; i<n; i++){
43         int x, y;
44         scanf("%d%d", &x, &y);
45         add(x, y); add(y, x);
46     }
47     dfs(s, 0);
48     while(m--){
49         int a, b;
50         scanf("%d%d", &a, &b);
51         printf("%d\n", lca(a, b));
52     }
53     return 0;
54 }
View Code

 

 同样是暴力,数据差别还是很大的。

 方法二:倍增,ST表

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=5e5+10;
 4 const int maxm=5e5+10;
 5 int n, m, s;
 6 struct edge{
 7     int to, next;
 8 }e[maxm<<1];
 9 int head[maxn],cnt;
10 void add(int u, int v){
11     e[++cnt].to=v;
12     e[cnt].next=head[u];
13     head[u]=cnt;
14 }
15 int lg[maxn];
16 void log_2(){//功能相当于log2函数 
17     lg[0]=-1;
18     for(int i=1; i<=n; i++)lg[i]=lg[i/2]+1;
19 }
20 
21 int dep[maxn], f[maxn][22];//通过dfs计算各个节点的深度,初始化f[i][0] 
22 void dfs(int u)
23 {
24     for(int i=head[u]; i; i=e[i].next){
25         int v=e[i].to;
26         if(f[u][0] == v)//避免双向重复dfs 
27             continue;
28         dep[v]=dep[u]+1;//深度计算 
29         f[v][0]=u;//初始化 
30         dfs(v);
31     }
32 }
33 void st_create(){//创建st表 
34     for(int j=1; j<=lg[n]; j++)
35         for(int i=1; i<=n; i++)
36             f[i][j]=f[f[i][j-1]][j-1];
37 }
38 
39 int lca(int x, int y)
40 {
41     if(dep[x] < dep[y])//不妨设x的深度 >= y的深度
42         swap(x, y);
43     while(dep[x] > dep[y])
44         x = f[x][lg[dep[x]-dep[y]]];
45     if(x == y)  //如果x是y的祖先,那他们的LCA肯定就是x了
46         return x;
47     for(int k=lg[n]; k>=0; --k)
48         if(f[x][k] != f[y][k])//因为我们要跳到它们LCA的下面一层,所以它们肯定不相等,如果不相等就跳过去。
49             x=f[x][k], y=f[y][k];
50     return f[x][0];//返回父亲节点
51 }
52 int main()
53 {
54     scanf("%d%d%d",&n, &m, &s);
55     for(int i=1; i<n; i++){
56         int x, y;
57         scanf("%d%d",&x, &y);
58         add(x, y);add(y, x);
59     }
60     log_2();
61     dfs(s);
62     st_create();
63     while(m--){
64         int a, b;
65         scanf("%d%d",&a, &b);
66         printf("%d\n", lca(a, b));
67     }
68     return 0;
69 }

 

posted @ 2022-04-19 15:57  TFLSNOI  阅读(72)  评论(0编辑  收藏  举报