题解 重排列
发现后手无论如何重排,不互质的数的相对位置是不变的
将不互质的数连 前\(\to\)后 的边,那么后手的操作相当于优先队列实现的拓扑排序
而先手的操作相当于为这些边定向
发现每个点双中的边是相互独立的
那么在每个点双中定向出一棵字典序最小的生成树即可
复杂度 \(O(n^2\log n)\),因为边有很多
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
bool vis[N];
const ll mod=998244353;
vector<pair<int, int>> g;
vector<int> e[N], to[N], ans;
int a[N], dsu[N], mn[N], cnt[N];
struct cmp{inline bool operator() (int i, int j) {return a[i]<a[j];}};
priority_queue<int, vector<int>, cmp> q;
inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
void dfs(int u) {
vis[u]=1;
for (auto v:e[u]) if (!vis[v]) {
dfs(v); g.pb({u, v}); to[u].pb(v);
}
}
signed main()
{
freopen("permutation.in", "r", stdin);
freopen("permutation.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
for (int i=1; i<=n; ++i) dsu[i]=i;
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
if (gcd(a[i], a[j])!=1)
e[i].pb(j), dsu[find(i)]=find(j);
for (int i=1; i<=n; ++i) sort(e[i].begin(), e[i].end(), [](int i, int j){return a[i]<a[j];});
a[0]=INF;
for (int i=1; i<=n; ++i) if (a[mn[find(i)]]>a[i]) mn[find(i)]=i;
for (int i=1; i<=n; ++i) if (!vis[i]) dfs(mn[find(i)]);
for (auto it:g) ++cnt[it.sec];
for (int i=1; i<=n; ++i) if (!cnt[i]) q.push(i);
while (q.size()) {
int u=q.top(); q.pop();
ans.pb(u);
for (auto v:to[u])
if (--cnt[v]==0) q.push(v);
}
for (auto it:ans) printf("%d ", a[it]);
printf("\n");
return 0;
}