题意:
给定n对点,每对点都有一条边相连,每次操作可以交换两个点的坐标,要求输出一个操作序列使得所有点对的边不相交。
思路:
将点按照x坐标排序,从左到右扫描,将第2k-1个和第2k个点连边,这里的连边指的是通过交换点的坐标。
用到两个数组match[u]=v表示u和v相连,id[i]=v表示排序后第i个位置是原本的第v个点,a[v]=i表示原本第v个点排序后位置是i。
假设第2k-1点是排序前的第u个点,第2k个点是排序前第uu个点,如果match[u]=uu,就不用操作。
设match[u]=v,
否则就要交换v和uu的坐标,也即是swap(id[i+1],id[a[v]]), 不过同时也要修改交换a数组,也就是swap(a[uu],a[v])。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 500020
#define M 40000
#define mod 10086
#define LL long long
#define Pi acos(-1.0)
#define inf 0x3f3f3f3f
struct point{
int x, y;
point(int x = 0, int y = 0) : x(x), y(y) {}
bool operator < (const point &b) const {
return x < b.x || (x == b.x && y < b.y);
}
void input(){
scanf("%d%d", &x, &y);
}
}p[N];
int id[N], match[N];
bool cmp(int i, int j) {
return p[i] < p[j];
}
int g[N][2];
int a[N];
int main(){
//freopen("tt.txt", "r", stdin);
int cas;
scanf("%d", &cas);
while(cas--){
int n;
scanf("%d", &n);
for(int i = 1; i <= 2*n; ++i){
p[i].input();
id[i] = i;
}
for(int i = 1; i <= n; ++i){
int u, v;
scanf("%d%d", &u, &v);
match[u] = v, match[v] = u;
}
int ans = 0;
sort(id + 1, id + 2 * n + 1, cmp);
for(int i = 1; i <= 2 * n; ++i)
a[id[i]] = i;
for(int i = 1; i < 2 * n - 2; i += 2){
int u = id[i], uu = id[i+1];
// cout << u << " " << uu << endl;
if( match[u] == uu )
continue;
ans++;
g[ans][0] = match[u], g[ans][1] = uu;
int v = match[u], vv = match[uu];
int x = a[v];
swap(a[id[i+1]], a[v]);
swap(id[i+1], id[x]);
}
printf("%d\n", ans);
for(int i = 1; i <= ans; ++i){
if(g[i][0] > g[i][1]) swap(g[i][0], g[i][1]);
printf("%d %d\n", g[i][0], g[i][1]);
}
}
return 0;
}