ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

LCA算法模板

2022-07-28 11:02:02  阅读:137  来源: 互联网

标签:访问 int vis 算法 LCA 节点 模板


LCA算法简介:

对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径。
LCA算法分为离线算法和在线算法
离线算法( off line algorithms),是指基于在执行算法前输入数据已知的基本假设,也就是说,对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。
在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。
LCA的离线算法主要指的是基于深度优先搜索的tarjan算法

Tarjan求LCA的基本思路:

Step 1.任选一个点为根节点,从根节点开始。
Step 2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。
Step 3.若是v还有子节点,返回2,否则下一步。
Step 4.合并v到u上。
Step 5.寻找与当前点u有询问关系的点v。
Step 6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。


例题详解:洛谷P3379【模板】最近公共祖先(LCA)https://www.luogu.com.cn/problem/P3379

核心代码如下:

 1 int Find(int u){//查找节点 u 的祖先节点 
 2     if(f[u] == u) return f[u];
 3     return f[u] = Find(f[u]);
 4 }
 5 void Tarjan(int u){
 6     vis[u] = 1;//表示节点 u 正在访问中 
 7     f[u] = u;//先给 f 数组赋上初始值,即为节点本身 
 8     for(int i = head1[u]; i; i = e1[i].next){
 9         int v = e1[i].to;
10         if(!vis[v]){//节点 v 没访问过则访问节点 v  
11             Tarjan(v);
12             f[v] = u;//回溯时将 f[v] 更改为其父亲节点 
13         }
14     }
15     vis[u] = -1;//标记节点 u 已访问 
16     for(int i = head2[u]; i; i = e2[i].next){
17         int v = e2[i].to;
18         if(vis[v] == -1) ans[(i + 1) / 2] = Find(v);//若节点 v 已变黑,则询问 v 的祖先,因为第1条边和第2条边相当于是同一条边,我们这里写成(i + 1)/ 2 将他们的祖先存放于同一数组中 
19     }
20 }

在这里我们用一个 f 数组表示节点 u 的父亲节点,vis 数组表示节点 u 的访问情况,若 vis[u] = 1 则表示节点 u 正在访问中,若 vis[u] = -1 则表示节点 u 已访问,若 vis[u] = 初始值0 则表示节点 u 还未访问。具体解释见代码。

下面是完整代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 5e5 + 5;
 4 struct Edge{
 5     int to, next;
 6 }e1[maxn << 1], e2[maxn << 1];//左移1表示 * 2 
 7 int n, root, q;
 8 int f[maxn];
 9 int head1[maxn], head2[maxn], len1, len2;
10 void Insert(int u, int v, Edge e[], int head[], int &len){
11     e[++len].to = v;
12     e[len].next = head[u];
13     head[u] = len;
14 }
15 void Read(){
16     scanf("%d%d%d", &n, &q, &root);
17     for(int i = 1; i < n; ++i){
18         int u, v;
19         scanf("%d%d", &u, &v);
20         Insert(u, v, e1, head1, len1); 
21         Insert(v, u, e1, head1, len1);
22     }
23     for(int i = 1; i <= q; ++i){
24         int u, v;
25         scanf("%d%d", &u, &v);
26         Insert(u, v, e2, head2, len2); //给询问的边建立邻接表关系便于进行询问 
27         Insert(v, u, e2, head2, len2);
28     }
29 }
30 int vis[maxn];
31 int ans[maxn];
32 int Find(int u){//查找节点 u 的祖先节点 
33     if(f[u] == u) return f[u];
34     return f[u] = Find(f[u]);
35 }
36 void Tarjan(int u){
37     vis[u] = 1;//表示节点 u 正在访问中 
38     f[u] = u;//先给 f 数组赋上初始值,即为节点本身 
39     for(int i = head1[u]; i; i = e1[i].next){
40         int v = e1[i].to;
41         if(!vis[v]){//节点 v 没访问过则访问节点 v  
42             Tarjan(v);
43             f[v] = u;//回溯时将 f[v] 更改为其父亲节点 
44         }
45     }
46     vis[u] = -1;//标记节点 u 已访问 
47     for(int i = head2[u]; i; i = e2[i].next){
48         int v = e2[i].to;
49         if(vis[v] == -1) ans[(i + 1) / 2] = Find(v);//若节点 v 已变黑,则询问 v 的祖先,因为第1条边和第2条边相当于是同一条边,我们这里写成(i + 1)/ 2 将他们的祖先存放于同一数组中 
50     }
51 }
52 void Print(int a[], int n){
53     for(int i = 1; i <= n; ++i)
54         printf("%d\n", a[i]);
55 }
56 int main(){
57     Read();
58     Tarjan(root);//从根节点开始查询 
59     Print(ans, q);
60     return 0;
61 }


 


 

完结撒花

 

 

标签:访问,int,vis,算法,LCA,节点,模板
来源: https://www.cnblogs.com/Hoshino-kaede/p/16526680.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有