CF1253D题解

修改日志

2022/12/16  22:422022/12/16\text{ }\text{ }22:42:修改了部分变量的名称。

思路

暴力+并查集,加一点点的优化即可。

这题的有点暴力的样子,对于每个节点 ii,初始 fi=if_i=i(常规的并查集初值)。唯一不同的是,每个代表元还要记两个变量 Xi,YiX_i,Y_i,其中 XiX_i 表示以 ii 为代表元的连通块的节点编号的最大值,YiY_i 表示以 ii 为代表元的连通块的节点编号的最小值。

输入一条边就合并一次连通块

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]);
}

接下来就是暴力+优化

对于每个节点 ii,查找他的代表元 pp,暴力搜一遍 YpXpY_p\sim X_p,查找其(指的是编号为 YpXpY_p\sim X_p 的节点)的代表元是不是 pp,如果不是,合并这两个并查集。

这里有个小细节:就是合并的并查集的代表元必须是 pp

就这么暴力?这题这么水?

其实并不是这样的,照这样暴力肯定会超时。

优化?

查找到的代表元 pp 以后,看看之前有没有访问到这个简单 pp,如果没有,标记为访问,去暴力搜。

复杂度:

玄学复杂度,但是可以过。

代码

#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);
}
posted @ 2022-11-27 10:02  fengxiaoyi  阅读(13)  评论(0)    收藏  举报  来源