quailty's Contest #1 道路修建 EXT(启发式合并)
题目链接 道路修建 EXT
考虑并查集的启发式合并,合并的时候小的子树的根成为大的子树的根的儿子。
可以证明这样整棵树的深度不会超过$logn$。
两个根合并的时候,产生的新的边的边权为当前的时间。
那么询问的时候答案就为$x$到$y$的最短路径上的所有边的边权最大值。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) const int N = 5e5 + 10; int T; int n, m; int num; int ans; int father[N], c[N], root[N], deep[N], sz[N]; int now; unordered_set <int> s[N]; int main(){ scanf("%d", &T); while (T--){ scanf("%d%d", &n, &m); num = n; ans = 0; rep(i, 0, n + 1){ s[i].clear(); s[i].insert(i); } rep(i, 1, n){ c[i] = 0; sz[i] = 1; father[i] = i; deep[i] = 1; root[i] = i; } rep(i, 1, m){ int op, x, y; scanf("%d%d%d", &op, &x, &y); x ^= ans; y ^= ans; if (op == 0){ if (root[x] == root[y]){ printf("%d\n", ans = num); continue; } --num; int fx = root[x], fy = root[y]; if (sz[fx] < sz[fy]){ swap(fx, fy); swap(x, y); } c[fy] = i; sz[fx] += sz[fy]; father[fy] = fx; sz[fy] = 0; for (auto u : s[fy]){ root[u] = fx; ++deep[u]; s[fx].insert(u); } s[fy].clear(); printf("%d\n", ans = num); } else{ if (root[x] != root[y]){ printf("%d\n", ans = 0); continue; } now = 0; if (deep[x] < deep[y]) swap(x, y); while (deep[x] != deep[y]){ now = max(now, c[x]); x = father[x]; } while (true){ if (x == y) break; now = max(now, c[x]); now = max(now, c[y]); x = father[x]; y = father[y]; } printf("%d\n", ans = now); } } } return 0; }