AtCoder[AGC010E] Rearranging

题目描述

有两人 \(A,B\),给定一个长度为 \(n\) 的序列 \(a\),有以下两个操作:

  1. \(A\) 将该序列任意排列;
  2. \(B\) 可以将任意两个互质的数交换位置。

\(A\) 希望最后序列字典序尽可能小,\(B\) 希望最后序列字典序尽可能大,请你输出最后序列。

数据范围:\(n\le 2\times 10^3\)\(a_i\le 10^8\)
时间范围:\(2000\operatorname{ms}\)

Solution

可以发现,对于任意两个不互质的数,他们的相对位置不会改变。

因此可以建出一张图,将任意两个不互质的数连边。

由于 \(A\) 要使字典序最小,所以 \(A\) 应该把较小的数放左边。

因此可以建出有向边然后拓扑排序。

\(B\) 要使字典序最大,可以在拓扑排序的时候优先队列维护。

时间复杂度为 \(O(n^2\log n)\)

Code

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=2020;
int Fir[maxn],Nxt[maxn*maxn],Too[maxn*maxn],rd[maxn],tot;
bool G[maxn][maxn],vis[maxn];
int n,a[maxn];
priority_queue<int> Q;
inline int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}
inline void add(int a,int b){
	Too[++tot]=b;Nxt[tot]=Fir[a];Fir[a]=tot;++rd[b];
}
inline void dfs(int u){
	vis[u]=1;
	for(int v=1;v<=n;++v)
		if(!vis[v]&&G[u][v]){
			add(u,v);
			dfs(v);
		}
}
inline void topsort(){
	for(int i=1;i<=n;++i)
		if(!rd[i])Q.push(i);
	while(!Q.empty()){
		int u=Q.top();Q.pop();
		printf("%d ",a[u]);
		for(int i=Fir[u];i;i=Nxt[i]){
			int v=Too[i];
			if(!--rd[v])Q.push(v);
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	for(int i=1;i<=n;++i)
		for(int j=i+1;j<=n;++j)
			if(gcd(a[i],a[j])!=1)
				G[i][j]=G[j][i]=1;
	for(int i=1;i<=n;++i)
		if(!vis[i])
			dfs(i);
	topsort();
	return 0;
}
posted @ 2021-05-31 21:45  7103  阅读(129)  评论(0)    收藏  举报