Tarjan求LCA
Tarjan求LCA是一种离线的LCA求法,它需要先知道所有的询问,之后通过dfs和并查集维护以求出LCA,这是一种线性的做法,如果有n个节点m次询问,那么复杂度为O(n+m)。
具体做法怎么做呢?其实就是dfs+并查集。
我们首先用链式前向星去存这棵树,存所有的询问关系(为什么一会再说),注意询问关系存成无向图。每个节点的父节点一开始设置为自己。然后我们从根结点开始dfs,优先去dfs自己的儿子,当dfs到一个没有儿子的节点的时候,我们开始遍历与这个节点有关的所有询问。(到这里就知道为什么用链式前向星了,因为这样才能线性跑完,否则的话就是m^2的复杂度)如果那个节点已经被访问过,那么这两个点的LCA就是那个节点沿着并查集向上找的父亲。如果没有访问过直接无视即可。
然后在节点向上返回的时候,把每个节点的父亲设置为自己上面的一个点,并且把这个点设置为已访问即可。
然后像所有离线一样……我们是需要记录每次求lca的编号的,最后输出按顺序输出即可。
Tarjan算法之所以可以做到线性处理,原因是在于他利用了只需要m次询问的关键。(当然不只是这些,人家倍增还多个log呢),综合来讲tarjan的复杂度是最优秀的,但是如果在m远远大于n的情况下,还是用RMQ求LCA是最好的。而倍增的话似乎更适用于预处理比较麻烦的时候。
至于树剖的话……一般树剖求LCA其实是顺手的事……好像很少有为了求LCA写树剖的吧……
看一下Tarjan求LCA的代码。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar('\n') #define pr pair<int,int> #define mp make_pair #define fi first #define sc second using namespace std; typedef long long ll; const int M = 500005; const int N = 10000005; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >='0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct edge { int next,to,id; }e[M<<2],q[M<<2]; int n,m,s,x,y,head[M],qhead[M],ecnt,qcnt,fa[M],lca[M]; bool vis[M]; int getfa(int x) { return fa[x] == x ? x : fa[x] = getfa(fa[x]); } void add(int x,int y) { e[++ecnt].to = y; e[ecnt].next = head[x]; head[x] = ecnt; } void addq(int x,int y,int g) { q[++qcnt].to = y; q[qcnt].next = qhead[x]; q[qcnt].id = g; qhead[x] = qcnt; } void tarjan(int x,int f) { for(int i = head[x];i;i = e[i].next) { if(e[i].to == f || vis[e[i].to]) continue; tarjan(e[i].to,x); fa[e[i].to] = x; } for(int i = qhead[x];i;i = q[i].next) { if(!vis[q[i].to]) continue; lca[q[i].id] = getfa(q[i].to); } vis[x] = 1; } int main() { n = read(),m = read(),s = read(); rep(i,1,n-1) x = read(),y = read(),add(x,y),add(y,x),fa[i] = i; fa[n] = n; rep(i,1,m) x = read(),y = read(),addq(x,y,i),addq(y,x,i); tarjan(s,s); rep(i,1,n) printf("%d\n",lca[i]); return 0; }
当你意识到,每个上一秒都成为永恒。