lca

来源:互联网 发布:苍山如海残阳如血 知乎 编辑:程序博客网 时间:2024/06/03 23:42
//目前会三个版本//1.倍增法。看挑战。//2.rmq.看挑战//3.离线 tarjan看http://www.cnblogs.com/JVxie/p/4854719.html//离线的基本原理是依据dfs的括号性的(看算法导论找dfs括号性)//在已知各个询问的时候我们阔以用并查集来辅助。。首先init()  另father[i]=i;然后开始dfs()//这里还需要一个visit[i]数组,用以记录以i为根的子树是否已经遍历完成。如果有询问(a,b)即询问a与b的公共祖先。那么在dfs时,如果以a为根的树//遍历完成时此时,刚刚要把visit[a]=1在此之前我们把father[a]修改为dfs时a的父亲节点然后看看visit[b]是否等于1如果visit[b]=1//那么find(a)函数返回的值就是最近公共祖先(什么是find(a)函数,看挑战程序设计竞赛并查集一张即知)如果visit[b]=0那么等到visit[b]要成为1时//再对b做相似的操作。//举例:点1连点2 点1连点3 1为根  询问(2,3)init()然后dfs(1)开始 到点2发现点2没有子节点了此时以2为根的子树遍历完成,准备把visit[2]=1,//在这之前father[2]=1;然后看visit[3]==1,现在visit[3]=0所以什么都不做然后visit[2]=1,然后回到1.此时1还没遍历完,开始遍历3,发现以3为根的//子树无子节点。遍历完成,准备visit[3]=1在这之前修改father[3]=1,然后看visit[2]==1(为什么知道是看2点呢?因为询问的是(2,3)嘛。//如果还有询问(3,4)当然还要看visit[4])此时,visit[2]=1所以find(3);函数返回值1,所以询问(2,3)的值为1.其他都一样。。。。很简单嘛。。//poj 1470(tarjan)  bzoj 1787//有坑点。。也许算不上。。主要是我太弱。。。。//此题如果用深度的绝对值来求距离就会造成错误 比如输入以下数据://6 1//1 2//2 5//2 6//6 3//6 4//5 3 4//正确答案是6 4 然而用绝对值就是6 2就呵呵了//此题除了上述我犯错外还有标记(1)那个地方写错了i>=0而不是i>0 还有就是把distfa[num][i]写成了distfa[i][num];//上述3个sb错误让我搞了一下午......//此题的分析就是注意如果a,b,c中先求(a,b)的lca q和(b,c)的lca p然后如果dist[q]>=dist[q]证明a,b,都能到q 运用这种性质阔以分析出(a,c)的lca要么是p,要么是q或者是比q还深的点分类讨论一下就有网上说的结果了。#include<iostream>#include<cstdio>#include<algorithm>using namespace std;struct edgee{int to;};edgee edge[2000050];int first[1000050], nextt[2000050];int distfa[1000050][22], dist[1000050];;int edgetot = 1,m,n;void addedgee(int from, int to){edge[edgetot].to = to;nextt[edgetot] = first[from];first[from] = edgetot;edgetot++;edge[edgetot].to = from;nextt[edgetot] = first[to];first[to] = edgetot;edgetot++;}void dfs(int num,int fa,int deep){dist[num] = deep; distfa[num][0] = fa;for (int i = 1; distfa[num][i - 1]; i++){int a = distfa[distfa[num][i - 1]][i - 1]; int b = distfa[num][i - 1];distfa[num][i] = distfa[distfa[num][i-1]][i - 1];}for (int i = first[num]; i; i = nextt[i]){int to = edge[i].to;if (!dist[to])dfs(to, num, deep + 1);}}int lca(int a, int b){if (dist[a] > dist[b])swap(a, b);int len = dist[b] - dist[a];for (int i = 0; len; len >>= 1, i++){int kind = len % 2;if (kind == 1)b = distfa[b][i]; }int temp = 0;for (int i = 21; i>=0; i--)//(1){if (distfa[a][i] != distfa[b][i])a = distfa[a][i], b = distfa[b][i];}if (a == b)temp = a;else    temp = distfa[a][0];return temp;}int getlenth(int a, int b){int r = lca(a, b);int k = dist[a] + dist[b] - 2 * dist[r];return k;}int main(){scanf("%d%d", &n, &m);for (int i = 1; i < n; i++){int a, b;scanf("%d%d", &a, &b);addedgee(a, b);}dfs(1, 0, 1);for (int i = 0; i < m; i++){int a, b, c;scanf("%d%d%d", &a, &b, &c);int k1 = lca(a, b); int k2 = lca(b, c); int k3 = lca(a, c);int len,k;if (k1 == k2)len = getlenth(a,k3) + getlenth(b , k3) + getlenth(c, k3), k = k3;else{if (k2 == k3)len = getlenth(a, k1) + getlenth(b, k1) + getlenth(c, k1), k = k1;elseif (k1 == k3)len = getlenth(a, k2) + getlenth(b, k2) + getlenth(c, k2), k = k2;}printf("%d %d\n", k, len);}return 0;}