LCA 的一些扩展算法

\(LCA\) 的一些扩展问题

一. 暴力

一步一步跳

算法:
(1) 求出每个点的深度
(2) 判断两个点是否重合,若是则该点就是 \(LCA\)
(3) 否则,选择深度大的点移动到它父亲

最坏情况下复杂度\(O(n)\)
随机情况下,树高度期望是\(O(n~log~n)\),可以放心食用
实现简单,是理想的对拍程序
只需要知道每个节点的父亲和深度,允许树动态改变

二. 倍增

不讲了

三. \(LCA\) 转化为 \(rmq\) 问题

\(dfs\) 遍历所有结点,建立三个数组
\(ver[i]\) : 表示 \(dfs\)\(i\) 个访问的节点(dfs序)
\(dep[i]\) : 表示 \(ver[i]\) 的层数
\(first[i]\) : 表示 \(ver[i]\) 第一次出现的下标

查找 \(x,y\) 的最近公共祖先 就相当于在 \([first[u],first[v]]\) 区间中找 \(dep\) 最小的

具体见代码

void dfs(int u, int Dep, int f) {
    fa[u] = f ;
    vis[u] = 1 ; node[++tot] = u ;  
	first[u] = tot ; dep[tot] = Dep ;
    rep(i, 0, siz(e[u]) - 1) {
    	int v = e[u][i] ;
    	if (vis[v]) continue ;
        dfs(v, Dep + 1, u) ;
        node[++tot] = u ; 
		dep[tot] = Dep ;
    }
}

void st(int n) {
    int k = (int) (log(double(n)) / log(2.0)) ;
    rep(i, 1, n) dp[i][0] = i ;
    rep(j, 1, k) 
	rep(i, 1, n + 1 - Pw[j]) {
        int a = dp[i][j - 1] ;
        int b = dp[i + Pw[j - 1]][j - 1] ;
        if (dep[a] < dep[b]) dp[i][j] = a ;
        else dp[i][j] = b ;
    }
}

int ask(int x, int y) {
    int k = (int) (log(double(y - x + 1)) / log(2.0)) ;
    int a = dp[x][k] ;
    int b = dp[y - Pw[k] + 1][k] ;
    if (dep[a] < dep[b]) return a ;
    return b ;
}

int lca(int u, int v) {
    int x = first[u], y = first[v] ;
    if (x > y) swap(x, y);
    int s = ask(x, y);
    return node[s] ;
}

四. \(Tarjan\)

Tarjan是离线算法,复杂度 \(O(n)\)

首先把所有的询问离线

之后遍历所有点,对于一个点 \(u\)

他的儿子 \(v\) 如果没有遍历过那么就把 \(u,v\) 合并

然后对于 \(u\) 的所有询问,如果可以回答的,答案就是对应 \(v\) 的并查集的根

Luogu 3379 模板题

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second

const int N = 500010 ;

int n, m, Rt, qid ;
int ans[N], fa[N], vis[N] ;
bool flg[N] ;

struct query {
	int t, qid ;
} ;

vector <int> e[N] ;
vector <query> q[N] ;

int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]) ;
}

void dfs(int u, int ft) {
	rep(i, 0, siz(e[u]) - 1) {
		int v = e[u][i] ;
		if (v == ft || vis[v]) continue ;
		dfs(v, u) ;
		fa[find(v)] = find(u) ;
		vis[v] = 1 ;
	}
	rep(i, 0, siz(q[u]) - 1) 
	if (!flg[q[u][i].qid] && vis[q[u][i].t]) {
		ans[q[u][i].qid] = find(q[u][i].t) ;
		flg[q[u][i].qid] = 1 ;
	}
}

signed main() {
	scanf("%d%d%d", &n, &m, &Rt) ;
	rep(i, 1, n) fa[i] = i ;
	rep(i, 1, n - 1) {
		int x, y ; scanf("%d%d", &x, &y) ;
		e[x].pb(y) ; e[y].pb(x) ;
	}
	rep(i, 1, m) {
		int x, y ; scanf("%d%d", &x, &y) ;
		q[x].pb((query){y, i}) ;
		q[y].pb((query){x, i}) ; 
	}
	dfs(Rt, 0) ;
	rep(i, 1, m) printf("%d\n", ans[i]) ;
	return 0 ;
}

posted @ 2020-07-14 11:30  harryhqg  阅读(133)  评论(0编辑  收藏  举报