CF455C Civilization 树的直径 + 并查集

题意翻译

给出一个由 nn 个点,mm 条边组成的森林,有 qq 组询问
1.1. 给出点 xx,输出点 xx 所在的树的直径
2.2. 给出点 x,yx,y,(如果 x,yx,y 在同一棵树中则忽略此操作)选择任意两点 u,vu,v,使得 uuxx 在同一棵树中且 vvyy 在同一棵树中。将 u,vu,v 之间连一条边,使得连边后的到的新树的直径最小

输入格式

第一行三个整数 n,m,qn,m,q,分别表示点的个数,边的个数和询问个数
接下来 mm 行,每行两个整数 x,yx,y,表示有一条链接点 x,yx,y 的边
接下来 qq 行,每行表示一条操作
操作11x1:1 x
操作22xy2:2 x y

输出格式

输出行数为操作 11 的个数
每行一个整数表示对应的操作一的答案

说明与提示

1m<n31051≤m<n≤3*10^5
1q31051≤q≤3*10^5
感谢 @_Wolverine 提供的翻译

样例输入

6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1

样例输出

4

题解

树的直径 + 并查集维护树的连通性
首先一遍树形 DPDP,求出每个并查集代表元素 xx 所在树的直径 c[x]c[x]
考虑合并两个树,假设这两棵树的连接点为 x,yx,y,代表元素分别为 fx,fyfx,fy
则合并后的最小直径为 max((c[fx]+1)/2+(c[fy]+1)/2,max(c[fx],c[fy]))max((c[fx]+1)/2+(c[fy]+1)/2,max(c[fx], c[fy]))
并查集维护即可

code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3e5 + 5;
int read() {
	int x = 0, f = 1; char ch;
	while(! isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(x = ch^48; isdigit(ch = getchar()); x = (x << 3) + (x << 1) + (ch ^ 48));
	return x * f;
}
template <class T> T Max(T a, T b) { return a > b ? a : b; }
template <class T> T Min(T a, T b) { return a < b ? a : b; }
struct Edge {
	int to;
	Edge *nxt;
	Edge(int to, Edge *nxt) : to(to), nxt(nxt) {}
} *head[N];
void add(int x, int y) {head[x] = new Edge(y, head[x]);}
int n, m, q, len, fa[N], c[N], g[N], d[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void dfs(int x, int f) {
	int m1 = 0, m2 = 0;
	for(Edge *i = head[x]; i; i = i->nxt) {
		if(i->to == f) continue; dfs(i->to, x);
		int tmp = d[i->to] + 1;
		d[x] = max(d[x], tmp);
		if(tmp > m1) m2 = m1, m1 = tmp;
		else if(tmp > m2) m2 = tmp;
	}
	g[x] = m1 + m2; len = max(len, g[x]);
}
void calc(int x) {len = 0; dfs(x, 0); c[x] = len;}
int main() {
	n = read(); m = read(); q = read();
	for(int i = 1; i <= n; ++ i) fa[i] = i;
	for(int i = 1, x, y; i <= m; ++ i) x = read(), y = read(), add(x, y), add(y, x), fa[find(x)] = find(y);
	for(int i = 1; i <= n; ++ i) if(fa[i] == i) calc(i);
	for(int i = 1, opt, x, y; i <= q; ++ i) {
		opt = read(); x = read();
		if(opt == 1) {printf("%d\n", c[find(x)]); continue;}
		y = read(); x = find(x); y = find(y);
		if(x == y) continue;
		int tmp = max((c[x] + 1) / 2 + (c[y] + 1) / 2 + 1, max(c[x], c[y]));
		fa[find(x)] = find(y); c[find(x)] = tmp;
	}
	return 0;
}