洛谷传送门
Codeforces传送门
题目大意
给你一个有 N N 个点的图。 一开始你已经有条边连接其中的一些点, 保证连接的点之间不会有环。 现在要求你支持两种操作:
- 1 A 1 A 询问A所在联通块的直径是多少。
- 2 A B 2 A B 将 A、B A 、 B 所在联通块之间任意连一条边使之成为一个联通块,并保证操作后联通块的直径最小。如果 A、B A 、 B 原本就在一个联通块中则忽略这个操作。
输入输出格式
输入格式
第一行三个整数 N,M,Q N , M , Q ,表示图中共有 N N 个点,原本就有条边, Q Q 个操作。
第行, 每行两个正整数 ai,bi a i , b i ,表示 ai a i 和 bi b i 间有一条边。
以下 Q Q 行,每行或 3 3 个正整数, 含义如上。
输出格式
对于每个操作, 输出一个整数表示该点所在联通块的直径。
输入输出样例
输入样例#1
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
输出样例#1
4
解题分析
两个联通块合并时,为了保证合并后直径尽量小,我们肯定会在两个联通块的直径的中点之间连一条边。
实际上我们并不需要真的连出这条边,只需要知道当前联通块的大小即可, 有如下公式:
所以我们先预处理出每个联通块的直径、点的归属情况,再用 DSU D S U 暴力合并维护即可。
总复杂度 O(N) O ( N ) 。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cmath>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 300050
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
int dot, line, q, cnt, root, pos;
int head[MX], dia[MX], bel[MX], dep[MX];
bool vis[MX];
struct Edge {int to, nex;} edge[MX << 1];
IN void addedge(const int &from, const int &to)
{edge[++cnt] = {to, head[from]}, head[from] = cnt;}
namespace DSU
{
int find(const int &now) {return bel[now] == now ? now : bel[now] = find(bel[now]);}
IN void combine(R int x, R int y) {bel[x] = find(y);}
}
void DFS(const int &now, const int &fa)
{
vis[now] = true;
if(dep[now] > dep[pos]) pos = now;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fa) continue;
dep[edge[i].to] = dep[now] + 1;
DFS(edge[i].to, now);
}
}
void merge(const int &now, const int &tar, const int &fa)
{//并查集初始化
bel[now] = tar;
for (R int i = head[now]; i; i = edge[i].nex)
{
if(edge[i].to == fa) continue;
merge(edge[i].to, tar, now);
}
}
void prework(const int &now)
{
pos = now; DFS(now, 0);//两次DFS求出直径
root = pos; dep[root] = 0; DFS(root, 0);
dia[root] = dep[pos]; merge(root, root, 0);
}
int main(void)
{
int a, b, typ;
in(dot), in(line), in(q);
for (R int i = 1; i <= line; ++i)
in(a), in(b), addedge(a, b), addedge(b, a);
for (R int i = 1; i <= dot; ++i) if(!vis[i]) prework(i);
W (q--)
{
in(typ);
if(typ & 1)
in(a), printf("%d\n", dia[DSU::find(a)]);
else
{
in(a), in(b);
R int x = DSU::find(a), y = DSU::find(b);
if(x == y) continue;
dia[y] = std::max(std::max(dia[y], dia[x]), (dia[y] + 1) / 2 + (dia[x] + 1) / 2 + 1);
DSU::combine(x, y);
}
}
}