Codeforces 1508D Swap Pass

可以发现,无论原本平面上的点是长成啥样、原本排列是长成啥样,我们都可以构造出一种方案,如下。

分析平面上的点的位置显然比分析排列要复杂得多,所以我们不妨从排列入手,做这么两件事:

1. 所有 $a_i = i$ 的点都可以被忽略掉(显然)。
2. 把 $i o a_i$ 连边,排列会被划分为若干个置换环。

接下来可以关注到如下事实:

性质 1:对于一个环,我们可以选择一个点 $u$,不断 $operatorname{swap}(u, a_u)$,最终所有的标签都会归位,并且这么做并不会产生任何交点。最终产生的将是一张菊花图,$u$ 是菊花图的花心。

那么,如果这整张图都只有一个环的话,题目就已经解决了。现在尝试解决多个环的情况。

性质 2:如果我们有两个不同的环,$u$ 属于第一个环中,$v$ 属于第二个环中,那么我们执行 $operatorname{swap}(u, v)$,这两个环就会被缩为一个环。

通过综合利用上面两个性质,我们可以得到一般的处理方式:

1. 首先,对于不同的环之间进行 $ ext{swap}$,直到它们缩为一个环。
2. 然后,再利用性质 1 的方式,把所有的标签归位。

接下来需要处理的问题是,如何使得交换产生的边互不相交。不妨从这些点构成一个凸多边形的情况着手分析。

任意选取一个中枢点 $ ext{pivot}$,现在,把其他所有点按照和中枢点的连线斜率排序,用 $ ext{Border Segments}$(边界线段)表示两个相邻点之间的连线,用 $ ext{Central Segments}$(中心线段)表示中枢点和其他点的连线。现在只使用边界线段来合并环,用中心线段来还原排列,这个问题就解决了。

这个做法对于非凸多边形适不适用呢?

好像也可以!

但中枢点真的也可以随便选取吗?

上图便是一个反例,出现边相交的原因其实是,出现了两个点的夹角大小 $>180^{circ}$。

于是,我们钦定最左下方的那个点为中枢点即可。

在实现中,可以通过并查集维护点是否在同一个环中。因为需要极角排序,所以时间复杂度 $O(n log n)$。

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
using namespace std;
const int N = 2005;
int n, m, pos[N];
vector<pair<int, int>> ans;
struct disjoint_sets_union {
	int fa[N];
	void Init() {
		for(int i = 1; i <= n; i++) {
			fa[i] = i;
		}
	}
	int Query(int p) {
		if(fa[p] == p) return p;
		return fa[p] = Query(fa[p]);
	}
	void Merge(int p, int q) {
		fa[p] = q;
	}
} dsu;
int pvtx, pvty, pvt;
struct point {
	int x, y, a, id;
	double agl;
	bool operator < (const point &oth) const {
		return agl < oth.agl;
	}
} p[N], q[N];
int main() {
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> p[i].x >> p[i].y >> p[i].a;
		p[i].id = i;
		if(p[i].a != i) {
			q[++m] = p[i];	
		}
	}
	if(!m) {
		cout << "0
";
		return 0;
	}
	dsu.Init();
	pvtx = q[1].x;
	pvty = q[1].y;
	pvt = 1;
	for(int i = 2; i <= m; i++) {
		if(q[i].x < pvtx) {
			pvtx = q[i].x;
			pvty = q[i].y;
			pvt = i;
		} else if(q[i].x == pvtx && q[i].y < pvty) {
			pvty = q[i].y;
			pvt = i;
		}
	}
	if(pvt != 1) {
		swap(q[1], q[pvt]);
	}
	for(int i = 2; i <= m; i++) {
		q[i].agl = atan2(q[i].y - pvty, q[i].x - pvtx);
	}
	sort(q + 2, q + m + 1);
	for(int i = 1; i <= m; i++) {
		int u = dsu.Query(q[i].id);
		int v = dsu.Query(q[i].a);
		if(u != v) {
			dsu.Merge(u, v);
		}
	}
	for(int i = 2; i < m; i++) {
		int u = dsu.Query(q[i].id);
		int v = dsu.Query(q[i + 1].id);
		if(u != v) {
			ans.pb(mp(q[i].id, q[i + 1].id));
			swap(q[i].a, q[i + 1].a);
			dsu.Merge(u, v);
		}
	}
	for(int i = 1; i <= m; i++) {
		pos[q[i].id] = i;
	}
	while(q[1].a != q[1].id) {
		int u = pos[q[1].a];
		ans.pb(mp(q[1].id, q[u].id));
		swap(q[1].a, q[u].a);
	}
	cout << ans.size() << endl;
	for(unsigned i = 0; i < ans.size(); i++) {
		cout << ans[i].fir << ' ' << ans[i].sec << endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/syksykCCC/p/CF1508D.html