Hash求无向图的桥

浅记一下,觉得颇有意趣。

我们记一个图 \(S\),构建其任意生成树 \(T\),对于任意一条边 \(E\),如果 \(E \in T\),我们将 \(E_u,E_v\) 两端点分别异或上一个随机哈希值,我们可以将哈希值看作一个集合 \(val_u\),这样对 \(T\) 进行 dfs,同时维护 \(val_u = \bigoplus_{v \in sub_u} val_v\)

\(\textbf{Theory}\):如果在 \(u \to v\) 时有 \(val_v\)\(u \to v\) 为桥。

证明:

假设 \(u \to v\) 不为割边,那么一定有 \(r\),使得 \(v \to r,r \notin sub_u\)

如果这条边是树边,那么树上一定有环,矛盾。

如果这条边是非树边,因为 \(v \to r\),有 \(val_v \cap val_r \ne \varnothing\),因为 \(r \notin sub_u\),必有 \(val_u \cap (val_v \cap val_r) \ne \varnothing\)

我们得证 \(val_u\) 并不为空集,即 \(val_u \ne 0\)

证毕。\(\square\)

以下是一份代码,可以过掉洛谷 P11360。

#include<bits/stdc++.h>
#include<random>
#define int long long
using namespace std;
const int N = 1e5 + 100;
int n, m, val[N], vis[N];
vector<int> g[N];
mt19937_64 R(time(0));
int fa[N];
int find(int x) {
	if (fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}
void dfs(int u, int fa) {
	vis[u] = 1;
	for (int e : g[u]) {
		if (e == fa) continue;
		dfs(e, u);
		val[u] ^= val[e];
		if (!val[e]) cout << u << ' ' << e << '\n';
	}
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= m; i++) {
		int u, v;
		cin >> u >> v;
		int fx = find(u), fy = find(v);
		if (fx != fy) {
			fa[fx] = fy;
			g[u].push_back(v), g[v].push_back(u);
		}
		else {
			int w = R();
			val[u] ^= w, val[v] ^= w;
		}
	}
	for (int i = 1; i <= n; i++) {
		if (!vis[i]) dfs(i, 0);
	}
	return 0;
}
posted @ 2025-11-25 20:24  OrangeRED  阅读(6)  评论(0)    收藏  举报