AGC010 E Rearranging

复盘 fateice 讲的题,来写篇题解造福社会

Description

一个数列。

先任意排序,然后按照相邻两数互质可以换位置的要求,最大化字典序。

同时,注意前面的任意排序是要最小化后者操作后的序列的字典序。

\(1\leq N\leq 2\cdot 10^3\)

Solution

N 不是很大,所以我们甚至可以直接 \(O(N^2\log N)\) 暴力判断任意两数之间是否互质。

一个很自然的想法,互质的数连边,这样能交换的数就有联系了。

但是,这是约束条件,并不是规定条件,(感性理解一下)就像不定方程而并非可解的方程,只知范围,不知答案。

所以换成不互质的数连边,这样不能交换的数有了联系。

所以先手的排序方式有了眉目,就是不互质的数里面,要从小到大。

但是重点是后手,怎么把先手留下的烂摊子收拾好。

因为不互质的数已经联系上了,所以能让一个很大的数跑到前面,前面的障碍都已经举了个牌子:你不要过来呀!此路不通,这已经规定死了其活动范围。

又因为所有大块之间没有任何影响,所以把每一大块不互质的数中最小的数丢到优先队列里,然后拓扑做剩下的。

这样既保证了先手排序的目的,又最大化了后手的操作的字典序。

Code

#include <bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int N = 2e3 + 10;
int n, a[N], fst[N], tot, du[N];
int eg[N][N], sig[N], cnt;
priority_queue<int> q;
struct edge {
	int nxt, to;
} e[N];
inline int gcd(int a, int b) {
	return (!b) ? a : gcd(b, a % b);
}
inline int read() {
	int s = 0, w = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
	return s * w;
}
inline void add(int u, int v) {
	e[++tot] = (edge) {fst[u], v};
	fst[u] = tot; ++du[v];
}
inline void dfs(int u) {
	sig[u] = cnt;
	for (int v = 1; v <= n; ++v) {
		if (!sig[v] && eg[u][v]) add(u, v), dfs(v);
	}
}
inline void toposort() {
	for (int i = 1; i <= n; ++i) {
		if (!du[i]) q.push(i);
	}
	while (!q.empty()) {
		int u = q.top(); q.pop();
		printf("%d ", a[u]);
		for (int i = fst[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (!(--du[v])) q.push(v);
		}
	}
}
int main() {
	n = read();
	for (int i = 1; i <= n; ++i) a[i] = read();
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j < i; ++j) {
			if (gcd(a[i], a[j]) != 1) {
				eg[i][j] = eg[j][i] = 1;
			}
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (!sig[i]) ++cnt, dfs(i);
	}
	toposort();
	return 0;
}

(好家伙这玩意思想真奇妙。。)

posted @ 2021-10-03 16:36  Illusory_dimes  阅读(63)  评论(0编辑  收藏  举报