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,最好取中心的点