Codeforces Round #260 (Div. 2) E.Civilization · 并查集 + 树的直径

32 篇文章 0 订阅

题解

题意: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值