前言
想到了相对顺序和连图。
想到了多个合并。
似乎没想到按照大小连出拓扑图,导致不知道怎么样才是最小不可操作的。
题目大意
一个长度为n的序列。
先手可以任意打乱,然后后手可以执行若干次以下操作:交换两个相邻且互质的数。
先手希望字典序最小,后手希望字典序最大,最后序列会变成啥样?
做法
容易发现对于不互质的一对数,相对位置永远不变。
我们让不互质的数之间连一条边,这样形成了图。
我们不妨从小到大对于每个点开始dfs其所在联通块,按照连向点的权值从小到大优先遍历,可以变成一个有向无环图。
容易发现我们只要做一个每次选一个最大的权值的拓扑排序,就是答案。
#include<cstdio>
#include<algorithm>
#include<queue>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=2000+10;
struct dong{
int x,y;
friend bool operator <(dong a,dong b){
return a.x<b.x;
}
} zlt;
priority_queue<dong> dl;
bool bz[maxn][maxn],pd[maxn];
int a[maxn],d[maxn];
int h[maxn],go[maxn*maxn],nxt[maxn*maxn];
int i,j,k,l,t,n,m,tot,now;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
void add(int x,int y){
d[y]++;
go[++tot]=y;
nxt[tot]=h[x];
h[x]=tot;
}
void dfs(int x){
pd[x]=1;
int i;
fo(i,1,n)
if (bz[x][i]&&!pd[i]){
add(x,i);
dfs(i);
}
}
int main(){
scanf("%d",&n);
fo(i,1,n) scanf("%d",&a[i]);
sort(a+1,a+n+1);
fo(i,1,n-1)
fo(j,i+1,n)
if (gcd(a[i],a[j])>1) bz[i][j]=bz[j][i]=1;
fo(i,1,n)
if (!pd[i]) dfs(i);
fo(i,1,n)
if (!d[i]){
zlt.x=a[i];
zlt.y=i;
dl.push(zlt);
}
fo(i,1,n){
zlt=dl.top();
dl.pop();
now=zlt.y;
printf("%d ",a[now]);
t=h[now];
while (t){
d[go[t]]--;
if (!d[go[t]]){
zlt.x=a[go[t]];
zlt.y=go[t];
dl.push(zlt);
}
t=nxt[t];
}
}
}