如 果 1 和 n 在 一 个 连 通 块 , 那 么 [ 1 , n ] 都 在 一 个 连 通 块 如果1和n在一个连通块,那么[1,n]都在一个连通块 如果1和n在一个连通块,那么[1,n]都在一个连通块
那 我 们 用 并 查 集 维 护 节 点 关 系 那我们用并查集维护节点关系 那我们用并查集维护节点关系
每 次 合 并 时 我 们 用 较 大 的 节 点 编 号 作 为 父 亲 每次合并时我们用\color{Red}{较大的节点编号作为父亲} 每次合并时我们用较大的节点编号作为父亲
那 么 当 枚 举 到 i 时 , [ i , f a [ i ] ] 都 要 在 一 个 连 通 块 那么当枚举到i时,[\ i,fa[i]\ ]都要在一个连通块 那么当枚举到i时,[ i,fa[i] ]都要在一个连通块
那 就 遍 历 [ i , f a [ i ] ] , 遍 历 到 的 节 点 又 会 有 父 亲 那就遍历[\ i,fa[i]\ ],遍历到的节点又会有父亲 那就遍历[ i,fa[i] ],遍历到的节点又会有父亲
在 f a [ i ] 和 遍 历 节 点 的 父 亲 动 态 取 最 大 值 , 满 足 i 到 最 大 值 都 是 连 通 块 在fa[i]和遍历节点的父亲动态取最大值,满足i到最大值都是连通块 在fa[i]和遍历节点的父亲动态取最大值,满足i到最大值都是连通块
还 是 非 常 巧 妙 地 还是非常巧妙地 还是非常巧妙地~
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+9;
int n,m;
int pre[maxn];
int find(int x){
return x==pre[x]?x:pre[x]=find(pre[x]);
}
void join(int q,int w){
int qq=find(q),ww=find(w);
if(qq>ww) swap(qq,ww);//大的作父节点
pre[qq]=ww;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) pre[i]=i;
while(m--)
{
int x,y;
cin>>x>>y;
join(x,y);
}
int ans=0;
for(int i=1;i<=n;i++)
{
int x=find(i);
while(i<x)
{
int y=find(i);
if(x!=y)
{
ans++;
join(x,y);
x=max(x,y);
}
i++;
}
}
cout<<ans;
}