洛谷P3379倍增LCA

传送门

#include <iostream> 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define re register
using namespace std ;
const int maxn = 5 * 1e5 + 4 ;

inline int read () {
	int f = 1 , x = 0 ;
	char ch = getchar () ;
	while (ch > '9' || ch < '0') {if(ch == '-') f = -1 ; ch = getchar () ;}
	while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0' ; ch = getchar () ;}
	return x * f ;
}

int n , m , s , x , y , a , b , c , d ;
int head[maxn] , tot ;

struct Edge {
	int from , to , next ;
}edge[maxn << 1] ;

inline void add (int u , int v) {
	edge[++tot].from = u ;
	edge[tot].to = v ;
	edge[tot].next = head[u] ;
	head[u] = tot ;
}

int dep[maxn] , f[maxn][21] ;
/*
	dep数组用来记录当前点的深度
	f[i][j]代表距离i 2^j的祖先
*/

inline void dfs (int x , int fa) {
	dep[x] = dep[fa] + 1 ;//更新深度
	f[x][0] = fa ;//更新父亲结点
	for(re int i = 1 ; (1 << i) <= dep[x] ; ++ i) 
	//预处理出f数组
		f[x][i] = f[f[x][i - 1]][i - 1] ;
		//x的2^i级祖先 就是 x的2^(i-1)级祖先的2^(i-1)级祖先。
	for(re int i = head[x] ; i ; i = edge[i].next) {
		int v = edge[i].to ;
		if(v != fa) dfs(v , x) ;
		//如果v是x的父亲,那么就说明这条边被访问过了,不能再回溯了
	}
}

inline int lca (int a , int b) {
	if(dep[a] < dep[b])  swap(a , b) ;//让a的深度更深
	for(re int i = 18 ; i >= 0 ; -- i) { //让a跳到跟b相同高度上
		if((1 << i) <= (dep[a] - dep[b])) {
			a = f[a][i] ;
		}
	}
	if(a == b)  return a ; //a,b在同一枝上时,此时a,b会在同一位置,返回任意一个 
	for(re int i = 18 ; i >= 0 ; -- i) { //从大到小跳
		if((1 << i) <= dep[a] && (f[a][i] != f[b][i])) {
			//如果不相等,就说明该节点的深度还是比LCA大
			a = f[a][i] ;
			b = f[b][i] ;
			//继续跳
		}
	}
	return f[a][0] ;
	//此时a和b不是同一个节点,但是a和b的父亲就是a和b的lca。
}

int main () {
	n = read () ; m = read () ; s = read () ; 
	for(re int i = 1 ; i <= n - 1 ; ++ i) {
		x = read () ; y = read () ;
		add(x , y) ;
		add(y , x) ;
	}
	dfs(s , 0) ;
	while(m--) {
		a = read () ; b = read () ;
		printf("%d
" , lca(a , b)) ;
	}
	return 0 ;
}
原文地址:https://www.cnblogs.com/Stephen-F/p/9933315.html