[CF1508D] Swap Pass

还是想了挺久的一道题。

首先要想到一个很明显的性质:如果我们固定一个点,让其他点与之交换,必不可能出现线段相交的情况。

所以考虑把一个点作为交换中心,那么交换的过程就是从 \(i \to a_i\),那就依次连边。

如果整个排列只有一个置换环,那就已经做完了。但是还有可能有多个环,所以根据题目给的数据,考虑将所有环合并起来。

如何合并?发现只需要交换两个不在同一个环的点即可,但如果随意交换就会导致可能有边相交,我们观察题意,注意到 任意三点不共线,那也就是说按照极角排序的顺序将剩余点排成一个序列,如果相邻的两个点不在一个环内,让它们相邻的两个点两两交换即可。

复杂度是 \(O(n \log n )\)

点击查看代码
#include<bits/stdc++.h>
#define fir first
#define sec second
#define int long long
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef pair<int,int> pir;
inline int read(){
	int x=0,f=1; char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1; ch=getchar();}
	while(isdigit(ch)){x=(x*10)+(ch^48); ch=getchar();}
	return x*f;
}
const int mod=998244353,inf=1e18,N=2e3+5;
int n,m;
int X,Y,pos,tot;
struct node{int x,y,id;}p[N],q[N];
inline bool cmp(node a,node b){
	return atan2(a.y-Y,a.x-X)<atan2(b.y-Y,b.x-X);
}
int a[N];
vector<int> ed[N];
int fa[N],vis[N],cnt;
pir res[N];
inline int getfa(int x){return (fa[x]!=x)?fa[x]=getfa(fa[x]):fa[x];}
inline bool merge(int x,int y){
	int a=getfa(x),b=getfa(y);
	if(a==b) return 0;
	fa[b]=a;
	return 1;
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=n;i++){
		int x=read(),y=read();
		a[i]=read();
		if(a[i]!=i) p[++tot]={x,y,i};
		merge(i,a[i]);
	}
	pos=1; 
	for(int i=2;i<=tot;i++) if(p[i].x<p[pos].x) pos=i;
	for(int i=1;i<=tot;i++) if(i!=pos) q[++m]=p[i];
//	for(int i=1;i<=tot;i++) cout<<getfa(i)<<' ';
//	puts("");
	X=p[pos].x,Y=p[pos].y;
	sort(q+1,q+m+1,cmp);
	for(int i=1;i<=m;i++){
		int nxt=(i==m)?1:i+1;
		int x=q[i].id,y=q[nxt].id;
		if(merge(x,y)) res[++cnt]=mkp(x,y),swap(a[x],a[y]);
	}
	int now=a[p[pos].id];
	for(int i=1;i<=m;i++){
		res[++cnt]=mkp(p[pos].id,now);
		now=a[now];
	}
	cout<<cnt<<'\n';
	for(int i=1;i<=cnt;i++){
		assert(res[i].fir<=n&&res[i].sec<=n);
		cout<<res[i].fir<<' '<<res[i].sec<<'\n';
	}
}



posted @ 2024-12-15 16:19  ~Cyan~  阅读(0)  评论(0编辑  收藏  举报