题解
题意:n个城市m条边,可能存在多个连通块,q个询问,两种操作:1.查询x城市所在的连通块的最长路,2.合并x所在的连通块和y所在的连通块
学习博客↓
codeforces 456 E. Civilization(并查集+数的直径)
在这里,最长路 = 树的直径
在查树的直径的同时进行并查集操作,可以优化时间
要使两个树合形成的新的直径最小,一定是两个直径中点相连,
新的直径长度 = max( max(dis[a], dis[b]), (dis[a] + 1) / 2 + (dis[b] + 1) / 2 + 1)
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n, m, q;
struct edge {
int to, next;
} e[N * 2];
int head[N], tot;
void add(int u, int v) {
e[++tot] = {v, head[u]};
head[u] = tot;
}
/*-------------树的直径 板子---------------*/
int f[N];
int dis[N];
int maxd, maxi;//直径 最远的点
int root = 0;
void dfs(int u, int fa, int step) {
f[u] = root; //优化 在dfs求树的直径时使用并查集
if (step > maxd) {
maxd = step;
maxi = u;
}
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if (v == fa)continue;
dfs(v, u, step + 1);
}
}
/*----------------------------*/
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
int main() {
ios::sync_with_stdio(0);
cin >> n >> m >> q;//n个点 m条边 q个询问
for (int i = 1; i <= n; ++i) {
f[i] = i;
}
while (m--) {
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
for (int i = 1; i <= n; ++i) {
if (f[i] == i) {
root = i;
maxd = -1;
dfs(i, -1, 0);
maxd = -1;
dfs(maxi, -1, 0);
dis[i] = maxd;
}
}
while (q--) {
int x, y, op;
cin >> op;
if (op == 1) {
cin >> x;
int fx = find(x);
cout << dis[fx] << endl;
} else {
cin >> x >> y;
int fx = find(x), fy = find(y);
if (fx == fy) continue;
else {
f[fy] = fx;
dis[fx] = max(max(dis[fx], dis[fy]), (dis[fx] + 1) / 2 + (dis[fy] + 1) / 2 + 1);
//两个连通块连起来后要让树的直径最小 那么肯定是两段中点连到一起最小
}
}
}
return 0;
}