专栏文章

题解:AT_abc233_f [ABC233F] Swap and Sort

AT_abc233_f题解参与者 3已保存评论 4

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
4 条
当前快照
1 份
快照标识符
@miqttuvq
此快照首次捕获于
2025/12/04 10:38
3 个月前
此快照最后确认于
2025/12/04 10:38
3 个月前
查看原文

思路:

首先,这是一道构造题。题目中说到,要求构造一种能够通过不超过限度次数的交换使得序列变得有序。由于题目中没有说必须要最优,所以只需要构造一种不超过限度的即可。
有一种较为优秀的构造方法:首先把各种交换关系抽象成一张图,如果有一个点和它想要变成的值不在同一个连通块里面,就说明无论怎样换都换不出有序序列。此时直接输出无解。然后,在图上每个连通块内部选取一些边,构造一棵生成树。从树上的叶子节点开始,每个点把它想要的权值通过和相邻节点交换权值换给它。处理完毕叶子节点,把处理完的节点“删掉”。然后按照同样的方式处理新的叶子节点。最后就可以把每个节点都换成它的对应值了。
为什么要换叶子节点呢?因为这种构造方式的核心就是每个处理完毕的节点都不会受到影响。这样每个点最多只会和剩下的所有点都交换一次,很大程度上避免了操作的重复。尝试取到极限数据,这样的构造方式刚好可以通过样例。

代码:

CPP
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,p[1005],a[200005],b[200005],fa[200005],edge[1005][1005];
vector<int>ans,g[200005];
bool vis[10005];
int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}
bool spp(int x,int fa,int num){
	if(p[x]==num)return true;
	for(int i:g[x]){
		if(i==fa)continue;
		if(spp(i,x,num)){
			ans.push_back(edge[i][x]);
			swap(p[x],p[i]);
			return true;
		}
	}
	return false;
}
void dfs(int x){
	vis[x]=1;
	for(int i:g[x]){
		if(!vis[i])dfs(i);
	}
	if(!spp(x,0,x)){
		cout<<-1;
		exit(0);
	}
}
signed main () {
	cin>>n;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
	cin>>m;
	for(int i=1;i<=m;i++){
		scanf("%lld%lld",&a[i],&b[i]);
		edge[a[i]][b[i]]=i;
		edge[b[i]][a[i]]=i;
		if(find(a[i])!=find(b[i])){
			fa[find(a[i])]=fa[find(b[i])];
			g[a[i]].push_back(b[i]);
			g[b[i]].push_back(a[i]);
		}
	}
	for(int i=1;i<=n;i++){
		if(find(p[i])!=find(i)){
			cout<<-1;
			return 0;
		}
	}
	for(int i=1;i<=n;i++){
		if(!vis[i])dfs(i);
	}
	cout<<ans.size()<<endl;
	for(int i:ans)cout<<i<<" ";
	return 0;
}

评论

4 条评论,欢迎与作者交流。

正在加载评论...