Codeforces Round #715 (Div. 1) D. Swap Pass

2 篇文章 0 订阅

D. Swap Pass
题意:每次选择平面上的两个点i,j,然后swap(a[i],a[j]),并在两点间连线,构造一种方案使得最后所有连线不相交(可以在节点处相交),并且a[i]=i

题解:
(1)先忽略a[i]=i的点
其余点可以分成下图若干个环
在这里插入图片描述
(2)考虑交换不同环上任意两点的a,如下图,
可以看到交换后的效果是把两个环合并成一个

交换同一环上相邻点的a,相当于缩环(自行作图)
在这里插入图片描述
(3那么我们就可以选择一个点为中心点,然后做一个极角排序,记中心点为x
每次找到a[x],然后考虑a[x]极角序下左右两侧的点,
如果有点y所属的环还没有合并,就swap(a[a[x]],a[y]),即将y所在的环合并进当前环,并给y所在的环标记。这种操作连的边是外圈的相邻点的间的边
(如果两个点都没有合并,任选一点)
然后swap(a[x],a[a[x]]),相当于缩环,这时连的边是中心点到外圈的边
如果两个点都已经合并,直接缩环。
在这里插入图片描述
(4)不相交证明
任意三点不共线,所以中心点可以到外圈点各连一条边,彼此不相交;外圈相邻点连线也不相交

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e3+5,inf=0x7fffffff;
int tx[maxn],ty[maxn],tk[maxn],cl[maxn],cnt;
int x0,y0,n,m;
int p[maxn],v[maxn],bk[maxn];
int miny,idy,now,nt,nt_back,top;

struct node{
	int x,y;
}ans[maxn<<1];

bool cross(int x1,int y1,int x2,int y2)
{
	if(1ll*x1*y2-1ll*x2*y1>0)return 1;
	else return 0;
}

bool cmp(int x,int y)
{
	int x1=tx[x],y1=ty[x];
	int x2=tx[y],y2=ty[y];
	return cross(x1-x0,y1-y0,x2-x0,y2-y0);
}

void sol(int x,int y)
{
	swap(tk[x],tk[y]);
	ans[++top].x=x; ans[top].y=y;
}

int main()
{
	int fl=1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&tx[i],&ty[i],&tk[i]);
		if(tk[i]!=i)fl=0;
	}
	
	if(fl){
		printf("0\n"); return 0;
	}
	for(int i=1;i<=n;i++){
		if(tk[i]==i)continue;
		p[++m]=i;
		if(cl[i]>0)continue;
		++cnt; now=i;
		while(tk[now]!=i){
			cl[now]=cnt;
			now=tk[now];
		}
		cl[now]=cnt;
	}
//	cout<<cnt<<endl;
	miny=inf;
	for(int i=1;i<=n;i++){
		if(tk[i]==i)continue;
		if(ty[i]<miny)miny=ty[i],idy=i;
	}
//	swap(tx[1],tx[idy]); swap(ty[1],ty[idy]); swap(tk[1],tk[idy]); swap(cl[1],cl[idy]); swap(id[1],id[idy]);
	for(int i=1;i<=m;i++){
		if(p[i]==idy){
			swap(p[1],p[i]); break;
		}
	}
	x0=tx[idy]; y0=ty[idy];
	sort(p+2,p+1+m,cmp);
	
//	cout<<m<<endl;
//	for(int i=1;i<=m;i++){
//		cout<<p[i]<<' ';
//	}
//	cout<<endl;
	
	for(int i=1;i<=m;i++)bk[p[i]]=i;
	now=idy;
	v[cl[now]]=1;
	do{
		nt=tk[now];
		nt_back=bk[nt];
		if(nt_back>2&&v[cl[p[nt_back-1]]]==0){
			v[cl[p[nt_back-1]]]=1;
			sol(nt,p[nt_back-1]);
			sol(now,nt);
		}
//		else if(nt_back==2&&v[cl[p[m]]]==0){
//			v[cl[p[m]]]=1;
//			sol(nt,p[m]);
//			sol(now,nt);
//		}
		else if(nt_back<m&&v[cl[p[nt_back+1]]]==0){
			v[cl[p[nt_back+1]]]=1;
			sol(nt,p[nt_back+1]);
			sol(now,nt);
		}
//		else if(nt_back==m&&v[cl[p[2]]]==0){
//			v[cl[p[2]]]=1;
//			sol(nt,p[2]);
//			sol(now,nt);
//		}
		else{
			sol(now,nt);
		}
//		if(tk[now]==idy)break;
	}while(tk[now]!=idy);
	printf("%d\n",top);
	for(int i=1;i<=top;i++)printf("%d %d\n",ans[i].x,ans[i].y);
	return 0;
}
/*
3
5 4 2
0 0 3
-3 -2 1

5
-1 -2 5
3 0 2
1 3 4
4 -3 3
5 2 1

5
-1 -2 5
3 0 3
1 3 4
4 -3 2
5 2 1

5
-1 -2 3
3 0 2
1 3 4
4 -3 1
5 2 5

8
0 0 4
-3 1 3
-2 1 2
-1 1 6
0 1 7
1 1 1
2 1 8
3 1 5
*/

tip:程序中中心点取y值最小的点,这样会有问题,但是能ac,最好取中心的点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值