帅气的传送门打么你想奥尔

如 果 1 和 n 在 一 个 连 通 块 , 那 么 [ 1 , n ] 都 在 一 个 连 通 块 如果1和n在一个连通块,那么[1,n]都在一个连通块 1n[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;
}