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;
}
(好家伙这玩意思想真奇妙。。)