题目链接:https://vjudge.net/contest/376346#problem/C
题意:给出n和m,n为结点个数,m为路径条数。如果,l和r之间能够互通,那么l到所有[l+1,r-1]结点都要能够互通
解题思路:先将已知的m条路径保存,以后面的结点为父结点进行合并。
然后遍历每一个结点i,找出该点的父结点tmp,根据要求区间[i,tmp]内的每一个点都要与i相通,遍历区间内的每一个结点,如果j的父亲结点等于tmp则相连,如果不等于tmp,则需要连接一条路径(j和tmp),ans++,同时更新tmp,tmp=max(tmp,find(j));
同时,因为已经保证了i到tmp之间全部互通,所以更新i,使得i=tmp即可
#include<iostream>
#include<cstdio>
using namespace std;
int pa[210000];
int n,m;
int ans;
int find(int x){
return x==pa[x]?x:pa[x]=find(pa[x]);
}
void unite(int x,int y){
int a=find(x);
int b=find(y);
if(a==b) return ;
if(a<b)
pa[a]=b;
else
pa[b]=a;
return ;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
pa[i]=i;
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
unite(u,v);
}
for(int i=1;i<=n;i++){
int tmp=find(i);
for(int j=i+1;j<tmp;j++){
int x=find(j);
if(x!=tmp){
unite(tmp,x);
ans++;
tmp=max(tmp,x);
}
}
i=tmp;
}
printf("%d\n",ans);
return 0;
}
类似的路径并查集问题:
CodeForces - 659E New Reform
https://vjudge.net/contest/376346#problem/J
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
int ans;
int pa[110000];
int rk[110000];
int num[110000];
int find(int x){
return x==pa[x]?x:pa[x]=find(pa[x]);
}
void unite(int x,int y){
int a=find(x);
int b=find(y);
if(a==b){
rk[a]++;
return ;
}
if(rk[a]>rk[b]){
pa[b]=a;
rk[a]+=rk[b];
}
else{
pa[a]=b;
rk[b]+=rk[a];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
pa[i]=i,rk[i]=1;
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
unite(a,b);
}
for(int i=1;i<=n;i++){
num[find(i)]++;
}
for(int i=1;i<=n;i++){
if(num[i]){
if(rk[i]<=num[i]){
ans+=num[i]-(rk[i]-1);
}
}
}
printf("%d\n",ans);
return 0;
}