并查集 - Harmonious Graph - CodeForces - 1253D

并查集 - Harmonious Graph - CodeForces - 1253D

题意:

给 一 个 无 向 图 , 点 和 边 都 是 2 × 1 0 5 范 围 。 要 使 每 个 连 通 块 内 的 点 编 号 连 续 , 问 至 少 再 加 几 条 边 。 给一个无向图,点和边都是2×10^5范围。要使每个连通块内的点编号连续,问至少再加几条边。 2×105使

首 行 输 入 n 和 m , 表 示 图 中 有 n 个 点 , m 条 边 。 首行输入n和m,表示图中有n个点,m条边。 nmnm

接 着 m 行 数 据 , 每 行 包 括 两 个 点 的 编 号 u i 、 v i , 表 示 u i 与 v i 连 通 。 接着m行数据,每行包括两个点的编号u_i、v_i,表示u_i与v_i连通。 muiviuivi

输 出 至 少 要 添 加 几 条 边 , 使 得 每 个 连 通 块 内 点 的 编 号 是 连 续 的 。 输出至少要添加几条边,使得每个连通块内点的编号是连续的。 使

Examples
Input:

14 8
1 2
2 7
3 4
6 3
5 7
3 8
6 8
11 12

Output:

1

Input:

200000 3
7 9
9 8
4 5

Output:

0

数据范围:

3 ≤ n ≤ 200000 , 1 ≤ m ≤ 200000 1 ≤ u i , v i ≤ n , u i ≠ v i T i m e   l i m i t : 1000 m s , M e m o r y   l i m i t : 262144 k B 3≤n≤200 000 , 1≤m≤200 000\\1≤u_i,v_i≤n, u_i≠v_i\\Time\ limit:1000 ms,Memory\ limit:262144 kB 3n2000001m2000001ui,vin,ui=viTime limit:1000msMemory limit:262144kB


分析:

首 先 利 用 并 查 集 先 建 立 好 节 点 之 间 的 连 通 关 系 。 首先利用并查集先建立好节点之间的连通关系。

为 了 保 证 每 个 连 通 块 中 的 编 号 是 连 续 的 , 我 们 需 要 知 道 最 小 编 号 和 最 大 编 号 分 别 是 多 少 。 为了保证每个连通块中的编号是连续的,我们需要知道最小编号和最大编号分别是多少。

可 以 在 建 图 的 过 程 中 , 每 次 都 将 两 个 节 点 中 编 号 较 大 的 节 点 作 为 根 节 点 。 可以在建图的过程中,每次都将两个节点中编号较大的节点作为根节点。

因 为 我 们 始 终 是 将 两 点 中 较 大 的 点 作 为 根 节 点 , 所 以 在 同 一 个 连 通 块 中 , 根 节 点 就 是 连 通 块 中 编 号 最 大 的 点 。 因为我们始终是将两点中较大的点作为根节点,所以在同一个连通块中,根节点就是连通块中编号最大的点。

这 样 对 于 每 个 节 点 i , 我 们 仅 需 查 询 [ i , P i ] 之 间 的 点 是 否 均 在 这 个 连 通 块 中 , 也 是 判 断 这 段 区 间 内 任 意 一 点 j 的 根 节 点 是 否 同 样 是 P i 。 其 中 P i 是 i 所 在 连 通 块 的 根 节 点 。 这样对于每个节点i,我们仅需查询[i,P_i]之间的点是否均在这个连通块中,\\也是判断这段区间内任意一点j的根节点是否同样是P_i。其中P_i是i所在连通块的根节点。 i[i,Pi]jPiPii

如 果 是 P i , 说 明 j 在 该 连 通 块 中 。 如果是P_i,说明j在该连通块中。 Pij

若 不 是 P i , 要 使 得 编 号 连 续 , 则 需 要 将 j 加 入 该 连 通 块 。 此 时 , 若 P j > P i , 我 们 需 要 将 根 节 点 改 成 P j 。 若不是P_i,要使得编号连续,则需要将j加入该连通块。此时,若P_j>P_i,我们需要将根节点改成P_j。 Pi使jPj>PiPj

这 样 , 我 们 从 小 到 大 考 虑 每 个 点 , 就 能 够 保 证 每 个 连 通 块 中 的 编 号 是 连 续 的 。 这样,我们从小到大考虑每个点,就能够保证每个连通块中的编号是连续的。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;

const int N=2e5+10;

int T,n,m;
int p[N];
bool vis[N];

int Find(int x)
{
    if(p[x]!=x) return p[x]=Find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n;i++) p[i]=i;
    
    int a,b,pa,pb;
    while(m--)
    {
        scanf("%d%d",&a,&b);
        pa=Find(a),pb=Find(b);
        if(pa>pb) swap(pa,pb);
        p[pa]=pb;
    }
    
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int pi=Find(i);
        for(int j=i;j<pi;j++)
        {
            int pj=Find(j);
            if(pi!=pj)
            {
                res++;
                if(pi<pj) swap(pi,pj);
                p[pj]=pi;
            }
        }
        i=pi;
    }
    
    cout<<res<<endl;
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值