倍增LCA

时间和空间复杂度分别是 O((n+q)logn) 和 O(nlogn) 。
1.DFS求每个节点的深度
2.倍增跳跃祖先节点预处理
3.如果两节点不在同一高度,则让较深的高度u跳跃到较浅的高度v来。
4.两个节点同时跳跃,先从大的跳跃幅度开始。
5.直到最后跳跃到最近公共祖先的下一层为止

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=1e3+5;
int father[maxn][40]={0};
vector<int> g[maxn];
int depth[maxn]={0};
int n,m;
int visit[maxn];
int inDeg[maxn];
void dfs(int u){
	visit[u]=1;
	for(int j=0;j<g[u].size();j++){
		int v = g[u][j];
		if(!visit[v]){
			depth[v]=depth[u]+1;
			dfs(v);
		}
	}  
}  //深搜,求出各点的深度 

void bz(){  //倍增 
	for(int j=1;j<=30;j++){
		for(int i=1;i<=n;i++){
			father[i][j] = father[father[i][j-1]][j-1];
		}
	}
} 

int LCA(int u,int v ){  //u,v是顶点编号 
	if(depth[u] < depth[v]) swap(u,v); //保证深度最大的点为u
	int d = depth[u] - depth[v];
	
	for(int i=0;i<=30;i++){  //使较深的点u跟v高度一致 
		if( (1<<i) & d)  u = father[u][i];
	} 
	if(u == v) return v;//如果深度一样时,两个点相同,则说明u在v的下面,直接返回。
	for(int i=29;i>=0;i--){
		if(father[u][i] != father[v][i]){  //如果两个值不等时,按照同样的速度往上跳 
			u = father[u][i];  
			v = father[v][i];
		}  //注意,这里没有直接跳出,而是反复跳到最近公共祖先的下一层为止才退出
	} 
	return father[u][0]; //再往上走一层就是最近公共祖先 
}
int main(){
	int u,v,x,y;
	scanf("%d %d",&n,&m);
	while(m--){
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		inDeg[v]++;
		father[v][0]=u;
	} 
	scanf("%d%d",&x,&y);
	int root=0;
	for(int i=1;i<=n && root==0;i++){
		if(inDeg[i]==0) root = i;
	}
	dfs(root);
	bz();
	
	int ans = LCA(x,y);
	printf("%d\n",ans);
	return 0;
} 

输入样例
5 4
1 3
2 3
3 4
3 5
4 5
输出样例
3
解释:
题目有5个顶点,4条边,查询4节点和5节点的最近公共祖先
输出结果为3

Tarjan

离线算法:先读入所有查询,在一次遍历中,把所有询问一次性解决。
时间复杂度: O ( n + q ) O(n+q) O(n+q)
P3379 【模板】最近公共祖先(LCA)
有3个测试点没过,懒得改了,等以后有时间了再修改吧。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e+5;
int f[maxn];
bool vis[maxn];
vector<int> G[maxn],Q[maxn];
struct qnode{
	int x;
	int y;
	int lca;
}query[maxn];
void init(){ //并查集初始化 
	for(int i=0;i<maxn;i++){
		f[i] = i;
	}
}
int find(int x){ //并查集的查找 
	if(f[x] == x) return x;
	else return f[x] = find(f[x]);
}
void dfs(int u) {
	vis[u] = true; 
	for(auto v : Q[u]){
		if(query[v].x == u){  //说明u就是当初存入的x,而需要查询的是y 
			if(vis[query[v].y]){
				query[v].lca = find(query[v].y); 
			}
		}else{
			if(vis[query[v].x]){
				query[v].lca = find(query[v].x); 
			}
		}
	}
	
	for(auto v : G[u]){
		if(vis[v]) continue; //父节点 跳过 
		dfs(v);
		f[v] = u;
		
	}
}
int main(){
	init();
	int n,m,s;
	scanf("%d%d%d",&n,&m,&s);
	int x,y;
	for(int i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		G[x].push_back(y);
		G[y].push_back(x);
	} 
	for(int i=1;i<=m;i++){
		scanf("%d%d",&query[i].x,&query[i].y);
		Q[query[i].x].push_back(i); //保存的是结构体的序号,每个结构体序号中有两个元素x,y 
		Q[query[i].y].push_back(i);
	}
	dfs(s);
	for(int i=1;i<=m;i++){
		printf("%d\n",query[i].lca);
	}
	return 0;
}