题目链接 道路修建 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;
}