LCA倍增算法

LCA 算法是一个技巧性很强的算法。

十分感谢月老提供的模板。

这里我实现LCA是通过倍增,其实就是二进制优化。

任何一个数都可以有2的阶数实现

例如16可以由1 2 4 8组合得到

5可以由1 2 4 组合得到

便于读者理解 我放一道例题吧

Problem F: 挑战迷宫

Description

小翔和小明正在挑战一个神奇的迷宫。迷宫由n个房间组成,每个房间的编号为1~n,其中1号房间是他们俩初始位置,
所有房间一共由n-1条路连接,使得房间两两之间能够相互达到(构成一棵树),每条路的长度为Wi。
  每当小翔和小明都在房间时,他们的神奇手机就能显示两人的位置(两人分别在哪两个房间),现在想请聪明的ACMer
快速地算出他们之间的最短距离。

Input

第一行输入整数n(0<n<=100000),表示迷宫有n个房间。
  接下来n-1行,每行输入3个整数u,v,w(1<=u,v<=n,u!=v,w<=10000),表示编号为u和v的房间之间有一条长为w的路。
  第n+1行输入整数m(0<m<=100000),表示有m次询问。
  接来下m行,每行输入2个整数u,v(1<=u,v<=n),表示小翔和小明当前所在房间的编号。

Output

对于每次询问的输出占一行,输出一个整数d表示小翔和小明之间的最短距离。

Sample Input

4
1 2 1
2 3 1
1 4 1
1
3 4

Sample Output

3

这是CSUST选拔赛的一题,表示当时不会LCA  菜的抠脚 (菜是原罪啊) 注意这题时间为1S N为1e6  最短路肯定是不行的,复杂度不行。n个点n-1条路 保证联通 其实就是每一个点到另外一个点有唯一的路径。然后这就是一个非常非常裸的LCA。
 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<queue>
 5 #include<vector>
 6
 7 using namespace std;
 8 #define maxn 100010
 9 struct node {
10     int x,y;
11     node(int x=0,int y=0):x(x),y(y){};
12 };
13 int rk[maxn],d[maxn],p[maxn][30];
14 vector<node>a[maxn];
15 int n;
16 void dfs(int u,int fa,int cnt) {
17     rk[u]=cnt;
18     p[u][0]=fa;
19     int len=a[u].size();
20     for (int i=0 ; i<len ; i++) {
21         int x=a[u][i].x;
22         if (x!=fa) {
23             d[x]=d[u]+a[u][i].y;
24             dfs(x,u,cnt+1);
25         }
26     }
27 }
28 void lca() {
29     for (int i=1 ; i<=n ; i++ ) {
30         for (int j=1 ; (1<<j)<=n ; j++) {
31             p[i][j]=-1;
32         }
33     }
34     for (int j=1 ; (1<<j)<=n ; j++) {
35         for (int i=1 ; i<=n ; i++) {
36             if (p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1];
37         }
38     }
39 }
40 int query(int x,int y) {
41     if (rk[x]<rk[y]) swap(x,y );
42     int k;
43     for (k=1 ; (1<<(1+k))<=rk[x] ; k++);
44     for (int i= k; i>=0 ; i--) {
45         if (rk[x]-(1<<i)>=rk[y]) x=p[x][i];
46     }
47     if (x==y) return x;
48     for (int i= k; i>=0 ; i--) {
49         if (p[x][i]!=-1 && p[x][i]!=p[y][i]){
50             x=p[x][i];
51             y=p[y][i];
52         }
53     }
54     return p[x][0];
55 }
56 int  main() {
57     int q,u,v,w;
58     while(scanf("%d", &n)!=EOF) {
59         for (int i = 1; i < n; i++) {
60             scanf("%d%d%d", &u, &v, &w);
61             a[u].push_back(node(v, w));
62             a[v].push_back(node(u, w));
63         }
64         dfs(1, -1, 0);
65         lca();
66         scanf("%d", &q);
67         while (q--) {
68             scanf("%d%d", &u, &v);
69             printf("%d\n", d[u]+d[v]-2*d[query(u, v)]);
70         }
71     }
72     return 0;
73 }
其中DFS(int u,int fa, int cnt)u表示当前节点 fa为他的父亲节点 cnt代表的是深度;int rk[maxn]记录深度  d[maxn] 记录节点  p[maxn][30]记录父亲节点的位置 lca() 这个就是精髓所在了 第一步初始化p[i][j]=-1;第二步就是二进制优化了 p[i][j]=p[p[i][j-1]][j-1]  表示i+2^j=i+2^(j-1)+2^(j-1)
前面都是预处理 第三步query(int x,int y) 求x,y的公共祖先。先判断深度,然后算出2^k <rk[x] 的k的最大值。if (rk[x]-(1<<i)>=rk[y]) x=p[x][i];将x的的深度向上回溯2^i 使之更接近rk[y]

for (int i= k; i>=0 ; i--) {
      if (p[x][i]!=-1 && p[x][i]!=p[y][i]){
         x=p[x][i];
         y=p[y][i];
      }
   }
   return p[x][0];

后面就是无脑回溯到公共祖先位置。

非常感谢月老的LCA倍增模板

以上就是我对LCA倍增算法的解析  如果读者还有不懂可以留言给我。

原文地址:https://www.cnblogs.com/qldabiaoge/p/8580449.html

时间: 2024-10-05 21:44:56

LCA倍增算法的相关文章

HDU 4547 LCA倍增算法

CD操作 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 1111    Accepted Submission(s): 297 Problem Description 在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录. 这里我们简化一下问题,假设只有一个根目录,

最近公共祖先 LCA 倍增算法

倍增算法可以在线求树上两个点的LCA,时间复杂度为nlogn 预处理:通过dfs遍历,记录每个节点到根节点的距离dist[u],深度d[u] init()求出树上每个节点u的2^i祖先p[u][i] 求最近公共祖先,根据两个节点的的深度,如不同,向上调整深度大的节点,使得两个节点在同一层上,如果正好是祖先结束,否则,将连个节点同时上移,查询最近公共祖先. void dfs(int u){ for(int i=head[u];i!=-1;i=edge[i].next){ int to=edge[i

图论——LCA倍增算法【转】

[转载]:http://www.cnblogs.com/OUSUO/p/3805715.html?utm_source=tuicool&utm_medium=referral 1. DFS预处理出所有节点的深度和父节点 inline void dfs(int u) { int i; for(i=head[u];i!=-1;i=next[i]) { if (!deep[to[i]]) { deep[to[i]] = deep[u]+1; p[to[i]][0] = u; //p[x][0]保存x的

算法笔记--lca倍增算法

算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; int anc[20][N]; int deep[N]; int h[N]; void dfs(int o,int u,int w) { if(u!=o)deep[u]=deep[o]+1,h[u]=h[o]+w; for(int j=0;j<g[u].size();j++) { if(g[u][j]!=o) { anc[0][g[u][j]]=u; for(int i=1;i<

LCA(倍增在线算法) codevs 2370 小机房的树

codevs 2370 小机房的树 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力.已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计

POJ 1330 Nearest Common Ancestors 倍增算法的LCA

POJ 1330 Nearest Common Ancestors 题意:最近公共祖先的裸题 思路:LCA和ST我们已经很熟悉了,但是这里的f[i][j]却有相似却又不同的含义.f[i][j]表示i节点的第2j个父亲是多少   这个代码不是我的,转自 邝斌博客 1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-9-5 9:45:17 4 File Name :F

codeforces 587C:(LCA倍增+维护最小值)

一开始直接无脑tarjan,回溯只能一层层往上走,太慢了,加了各种优化还是TLE 后来了解到LCA倍增法(在线).复杂度其实相比LCA转RMQ以及tarjan是要稍差一些,但是其中能同步维护的只有LCA倍增,很神奇的算法 #include"cstdio" #include"queue" #include"cmath" #include"stack" #include"iostream" #include&q

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先