题解 重排列

传送门
AGC010E Rearranging

发现后手无论如何重排,不互质的数的相对位置是不变的
将不互质的数连 前\(\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;
}
posted @ 2022-03-29 14:42  Administrator-09  阅读(1)  评论(0编辑  收藏  举报