CF 455. C---并查集+树的直径

Andrew plays a game called “Civilization”. Dima helps him.

The game has n cities and m bidirectional roads. The cities are numbered from 1 to n. Between any pair of cities there either is a single (unique) path, or there is no path at all. A path is such a sequence of distinct cities v1, v2, …, vk, that there is a road between any contiguous cities vi and vi + 1 (1 ≤ i < k). The length of the described path equals to (k - 1). We assume that two cities lie in the same region if and only if, there is a path connecting these two cities.

During the game events of two types take place:

Andrew asks Dima about the length of the longest path in the region where city x lies.
Andrew asks Dima to merge the region where city x lies with the region where city y lies. If the cities lie in the same region, then no merging is needed. Otherwise, you need to merge the regions as follows: choose a city from the first region, a city from the second region and connect them by a road so as to minimize the length of the longest path in the resulting region. If there are multiple ways to do so, you are allowed to choose any of them.
Dima finds it hard to execute Andrew’s queries, so he asks you to help him. Help Dima.

Input
The first line contains three integers n, m, q (1 ≤ n ≤ 3·105; 0 ≤ m < n; 1 ≤ q ≤ 3·105) — the number of cities, the number of the roads we already have and the number of queries, correspondingly.

Each of the following m lines contains two integers, ai and bi (ai ≠ bi; 1 ≤ ai, bi ≤ n). These numbers represent the road between cities ai and bi. There can be at most one road between two cities.

Each of the following q lines contains one of the two events in the following format:

1 xi. It is the request Andrew gives to Dima to find the length of the maximum path in the region that contains city xi (1 ≤ xi ≤ n).
2 xi yi. It is the request Andrew gives to Dima to merge the region that contains city xi and the region that contains city yi (1 ≤ xi, yi ≤ n). Note, that xi can be equal to yi.
Output
For each event of the first type print the answer on a separate line.
cf题目

题意
在无向图中,进行2种操作
1.使两个结点x,y所在的区域连通并在加边时最小化联通域的最长路径(树的直径)
2.查询结点x所在联通域内树的直径(可能有环)

题解:并查集记录联通域,dfs搜索树的直径以及直径的中心结点,作为次区域的父节点,连接区域时在父节点间加边可使联通后的直径最小

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define ll long long

const int maxn = 300011;

struct EDGE {
    int v, next;
}e[maxn << 1];
int head[maxn], en;
int f[maxn], md[maxn];
int n, m, q;
int maxd, maxi, thefather;
void add(int x, int y) {
    e[en].v = y;
    e[en].next = head[x];
    head[x] = en++;
}

int getfather(int x) {
    return f[x] == x ? x : f[x] = getfather(f[x]);
    //递归搜索直到搜到父节点(我等于我自己)
    //这样的好处是,每次进行区域合并不用再更新所有节点的父节点
}

void link(int x, int y) {
    int fx, fy;
    fx = getfather(x);
    fy = getfather(y);
    if (fx == fy)return;
    if (md[fx] < md[fy])swap(fx, fy);
    md[fx] = max(md[fx], (md[fx] + 1) / 2 + (md[fy] + 1) / 2 + 1);
    //树的直径公式
    //就是把[x块的最长路的中间点]连接[y块的最长路的中间点],
    //得到的新路的长度是(x块最长路的一半)加上(y块最长路的一半)加上1。
    f[fy] = fx;//只需要指定一次父节点
}
//搜寻树的直径和直径的中间点
void dfs(int x, int prex, int step) {
    f[x] = thefather;//统一根节点(并查集)
    if (step > maxd) {
        maxd = step;//更新最长路径
        maxi = x;//最长路径的末端节点?
    }
    for (int i = head[x]; i != -1; i = e[i].next)
        if (e[i].v != prex) dfs(e[i].v, x, step + 1);//防止搜索父节点
}

void check(int a[], int n) {
    for (int i = 0; i < n; i++)
        printf("%d ", a[i]);
    puts("");
}

int main()
{
    int i, x, y, z;
    while (scanf("%d%d%d", &n, &m, &q) != EOF) {
        memset(head, -1, sizeof(head));
        en = 0;
        REP(i, m) {//初始加边
            scanf("%d%d", &x, &y);
            add(x, y);
            add(y, x);
        }
        for (i = 1; i <= n; i++)
            f[i] = i;//根节点=本节点
        for (i = 1; i <= n; i++)//无论如何,一条最大路径的两端都将经过搜索,并寻找出最大直径
            if (f[i] == i) {//统一根节点
                maxd = -1;
                thefather = i;
                dfs(i, -1, 0);//在一次dfs内搜索完一棵树
                maxd = -1;
                thefather = maxi;//以最长路径(直径)的另一端为父节点此时可能搜索到的直径一定>=原直径
                dfs(thefather, -1, 0);//再次搜索(结果可能会不一样)!!!
                //这一轮下来一定能有最大路径?
                md[thefather] = maxd;//md[i]代表以i为一端的最大路径长度
                //最大路径更新完成,树根产生
            }
        REP(i, q) {
            scanf("%d", &z);
            if (z == 1) {
                scanf("%d", &x);
                printf("%d\n", md[getfather(x)]);
            }
            else {
                scanf("%d%d", &x, &y);
                link(x, y);
                //                check(md,n+1);
                //                check(f,n+1);
            }
        }
    }
    return 0;
}*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值