CF455C Civilization Solution

这是一道并查集的简单维护题
题目链接(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 ux所在子树, v ∈ y v \in y vy所在子树 ,将 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,22dx+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;
}

Thanks!

你可能感兴趣的:(c++)