CF691D 题解

题目传送门

题目分析

随机跳到的一道很有意思的题。

显然在这 \(m\) 对交换中,我们可以将它们划为多个集合,其中所有的数都是可以互相交换的。

那么如何去划分这些集合?没错,并查集。把可以互换的数当成点并连边,接着就可以并查集找连通块。

已经满足了一个条件,接着我们去找连通块中字典序最大的排列。显然在集合中再进行一次由大到小的排序就可以了。对于这一步,所有并查集题解似乎都不约而同用了滚动数组,但是使用优先队列会更加简单,不需要自己进行额外的排序。

贴上代码

#include<bits/stdc++.h>
// #define int long long
#define debug puts("Shiina_Mashiro_kawaii")
#define ok puts("Yes")
#define no puts("No")
using namespace std;
int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-48;
		c=getchar();
	}
	return x*f;
}
const int maxn=1e6+5;
int n,m;
int a[maxn];
int f[maxn];
priority_queue<int> q[maxn];
// int cnt_edge,head[maxn];
// struct edge{
	// int to,nxt;
// }e[maxn<<1];
// void add(int u,int v){
	// e[++cnt_edge].nxt=head[u];
	// e[cnt_edge].to=v;
	// head[u]=cnt_edge;
// }
int find(int x){
	if(x!=f[x]) f[x]=find(f[x]);
	return f[x];
}
void unity(int x,int y){f[find(x)]=find(y);}
inline void init(){
	n=read();m=read();
	for(register int i=1;i<=n;++i) f[i]=i;
	for(register int i=1;i<=n;++i) a[i]=read();
	while(m--){
		int u=read(),v=read();
		unity(u,v);
	}
}
int main(){
	init();
	for(register int i=1;i<=n;++i) q[find(i)].push(a[i]);
	for(register int i=1;i<=n;++i){
		int fi=find(i);
		printf("%d ",q[fi].top());q[fi].pop();
	}
}
posted @ 2023-01-13 20:57  yizhixiaoyun  阅读(41)  评论(0)    收藏  举报