常用图论算法

注意: \color{red}{注意:} 注意: 个人博客传送门

图论(Graph Theory)

LCA (最近公共祖先)

查找最近公共祖先的方法:

  1. 向上标记法 O ( n ) O(n) O(n)

  2. 倍增法 O ( n l o g n ) O(nlogn) O(nlogn)

    • f a [ i , j ] fa[i, j] fa[i,j] 表示从 i i i 开始,向上走 2 j 2^j 2j 步所能到达的节点, 0 ≤ j ≤ l o g ( n ) 0 \le j \le log(n) 0jlog(n)
    • d e p t h [ i ] depth[i] depth[i] 表示深度

    步骤:

    1. 先将两个点跳到同一层。
    2. 让两个点同时向上跳,直到跳到他们的最近公共祖先的下一层。

    预处理 O ( n l o g n ) O(nlogn) O(nlogn)

    查询 O ( l o g n ) O(logn) O(logn)

  3. T a r j a n Tarjan Tarjan 离线求 L C A LCA LCA O ( n + m ) O(n+m) O(n+m)

练习

祖孙询问

算法:倍增法求 l c a lca lca

#include <bits/stdc++.h> 

using i64 = int64_t;

constexpr int N = 4E4 + 10;
int f[N][20], dep[N];

int lca(int x, int y) {
	if (dep[x] < dep[y]) {
		std::swap(x, y);
	}

	for (int i = 19; i >= 0; i--) {
		if (dep[f[x][i]] >= dep[y]) {
			x = f[x][i];
		}		
	}

	if (x == y) return x;

	for (int i = 19; i >= 0 && y != x; i--) {
		if (f[x][i] != f[y][i]) {
			x = f[x][i];
			y = f[y][i];
		}
	}
	return f[x][0];
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    int rt = -1;
    std::vector<std::vector<int>> adj(N);
    for (int i = 0; i < n; i++) {
    	int u, v;
    	std::cin >> u >> v;

    	if (v == -1) {
    		rt = u;
    		continue ;
    	}

    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }
 	
    auto dfs = [&](auto self, int u, int fa) -> void {
    	f[u][0] = fa;
    	dep[u] = dep[f[u][0]] + 1;

    	for (int i = 1; i < 20; i++) {
    		f[u][i] = f[f[u][i - 1]][i - 1];
    	}

    	for (auto x : adj[u]) {
    		if (x == fa) continue ;
    		self(self, x, u);
    	}
    };
    dfs(dfs, rt, 0);

 	int q;
 	std::cin >> q;

 	while (q--) {
 		int x, y;
 		std::cin >> x >> y;

 		int ans = 0;
 		if (lca(x, y) == x) {
 			ans = 1;
 		} else if (lca(x, y) == y) {
 			ans = 2;
 		}
 		std::cout << ans << "\n";
 	}

    return 0;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值