题意:
给 一 个 无 向 图 , 点 和 边 都 是 2 × 1 0 5 范 围 。 要 使 每 个 连 通 块 内 的 点 编 号 连 续 , 问 至 少 再 加 几 条 边 。 给一个无向图,点和边都是2×10^5范围。要使每个连通块内的点编号连续,问至少再加几条边。 给一个无向图,点和边都是2×105范围。要使每个连通块内的点编号连续,问至少再加几条边。
首 行 输 入 n 和 m , 表 示 图 中 有 n 个 点 , m 条 边 。 首行输入n和m,表示图中有n个点,m条边。 首行输入n和m,表示图中有n个点,m条边。
接 着 m 行 数 据 , 每 行 包 括 两 个 点 的 编 号 u i 、 v i , 表 示 u i 与 v i 连 通 。 接着m行数据,每行包括两个点的编号u_i、v_i,表示u_i与v_i连通。 接着m行数据,每行包括两个点的编号ui、vi,表示ui与vi连通。
输 出 至 少 要 添 加 几 条 边 , 使 得 每 个 连 通 块 内 点 的 编 号 是 连 续 的 。 输出至少要添加几条边,使得每个连通块内点的编号是连续的。 输出至少要添加几条边,使得每个连通块内点的编号是连续的。
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 3≤n≤200000,1≤m≤2000001≤ui,vi≤n,ui=viTime limit:1000ms,Memory 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]之间的点是否均在这个连通块中,也是判断这段区间内任意一点j的根节点是否同样是Pi。其中Pi是i所在连通块的根节点。
如 果 是 P i , 说 明 j 在 该 连 通 块 中 。 如果是P_i,说明j在该连通块中。 如果是Pi,说明j在该连通块中。
若 不 是 P i , 要 使 得 编 号 连 续 , 则 需 要 将 j 加 入 该 连 通 块 。 此 时 , 若 P j > P i , 我 们 需 要 将 根 节 点 改 成 P j 。 若不是P_i,要使得编号连续,则需要将j加入该连通块。此时,若P_j>P_i,我们需要将根节点改成P_j。 若不是Pi,要使得编号连续,则需要将j加入该连通块。此时,若Pj>Pi,我们需要将根节点改成Pj。
这 样 , 我 们 从 小 到 大 考 虑 每 个 点 , 就 能 够 保 证 每 个 连 通 块 中 的 编 号 是 连 续 的 。 这样,我们从小到大考虑每个点,就能够保证每个连通块中的编号是连续的。 这样,我们从小到大考虑每个点,就能够保证每个连通块中的编号是连续的。
代码:
#include
#include
#include
#include
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;
}