这是一道并查集的简单维护题
题目链接(luogu)
CodeForces
给出一棵森林,有两种操作
1 。 1 ^。 1。 给出点 x x x,输出点 x x x 所在的树的直径
2 。 2 ^。 2。 给出点 x , y x,y x,y,(如果 x , y x,y x,y 在同一棵树中则忽略此操作)选择任意两点 u ∈ x u \in x u∈x所在子树, v ∈ y v \in y v∈y所在子树 ,将 u , v u,v u,v 之间连一条边,使得连边后的到的新树的直径最小。
首先考虑查询操作:
我们用两遍 d f s dfs dfs 求出每一颗子树树的直径,并顺便更新每一个点的从属情况
接下来考虑合并操作:
因为是使新树的直径最小
而这个直径的更新从三个方面来
d n e w = m i n ( d x , d y , ⌈ d x 2 ⌉ + ⌈ d y 2 ⌉ 2 + 1 ) d_{new} = min(d_x, d_y, \frac{\lceil \frac{d_x}{2} \rceil + \lceil \frac{d_y}{2} \rceil}{2} + 1) dnew=min(dx,dy,2⌈2dx⌉+⌈2dy⌉+1)
故可以在合并的时候将新树的 d d d维护出来
具体实现见代码:
const int N = 3e5 + 5;
int n, m, q, mxindex, mxval, fa[N], d[N], belong;
vector<int> G[N];
void dfs(int x, int pre, int sum)
{
fa[x] = belong;
if (sum > mxval)
{
mxindex = x;
mxval = sum;
}
for (auto it : G[x])
if (it != pre)
dfs(it, x, sum + 1);
}
int findset(int x)
{
return fa[x] == x ? fa[x] : fa[x] = findset(fa[x]);
}
void merge(int x, int y)
{
x = findset(x), y = findset(y);
if (x != y)
{
if (d[x] < d[y])
swap(x, y);
d[x] = max(d[x], (d[x] + 1) / 2 + (d[y] + 1) / 2 + 1);
fa[y] = x;
}
}
int main()
{
rd(n), rd(m), rd(q);
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 1, x, y; i <= m; i++)
rd(x), rd(y), G[x].push_back(y), G[y].push_back(x);
for (int i = 1; i <= n; i++)
if (fa[i] == i)
{
belong = i;
mxindex = mxval = 0;
dfs(i, 0, 0);
belong = mxindex;
dfs(mxindex, 0, 0);
d[belong] = mxval;
}
while (q--)
{
int opt, x, y;
rd(opt), rd(x);
if (opt == 1)
printf ("%d\n", d[findset(x)]);
else
rd(y), merge(x, y);
}
return 0;
}