CF1253D题解
修改日志
:修改了部分变量的名称。
思路
暴力+并查集,加一点点的优化即可。
这题的有点暴力的样子,对于每个节点 ,初始 (常规的并查集初值)。唯一不同的是,每个代表元还要记两个变量 ,其中 表示以 为代表元的连通块的节点编号的最大值, 表示以 为代表元的连通块的节点编号的最小值。
输入一条边就合并一次连通块
void H(int k1,int k2){//合并操作
int x=Find(k1),y=Find(k2);
fa[x]=y;
mn[y]=min(mn[x],mn[y]);
mx[y]=max(mx[x],mx[y]);
}
接下来就是暴力+优化
对于每个节点 ,查找他的代表元 ,暴力搜一遍 ,查找其(指的是编号为 的节点)的代表元是不是 ,如果不是,合并这两个并查集。
这里有个小细节:就是合并的并查集的代表元必须是 。
就这么暴力?这题这么水?
其实并不是这样的,照这样暴力肯定会超时。
优化?
查找到的代表元 以后,看看之前有没有访问到这个简单 ,如果没有,标记为访问,去暴力搜。
复杂度:
玄学复杂度,但是可以过。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,num,si[200010],ans;
int fa[200010],mx[200010],mn[200010];
bool v[200010];
int Find(int k){
if(fa[k]==k) return k;
return fa[k]=Find(fa[k]);
}
void H(int k1,int k2){
int x=Find(k1),y=Find(k2);
fa[x]=y;
mn[y]=min(mn[x],mn[y]);
mx[y]=max(mx[x],mx[y]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
fa[i]=mx[i]=mn[i]=i;
for(int i=1;i<=m;i++){
int x1,x2;
scanf("%d%d",&x1,&x2);
H(x1,x2);
}
for(int i=1;i<=n;i++){
int f=Find(i);
if(v[f]) continue;
v[f]=1;
for(int j=mn[f]+1;j<mx[f];j++){
int fj=Find(j);
if(fj!=f) H(fj,f),ans++;
}
}
return !printf("%d",ans);
}

浙公网安备 33010602011771号