题意
有一个数组A。现在先手可以把整个数组任意排列,然后后手可以任意次交换两个相邻且互质的数。先手要使得字典序尽量小,后手要使得字典序尽量大。问最后的序列长什么样。
n<=2000,Ai<=10^8
分析
首先很容易想到两个不互质的数的相对位置不能发生改变。
考虑在两个不互质的数之间连边,于是可以得到若干个连通块。
先手的操作可以看成是对每条边定向,使得任意一个连通块都是一个有向无环图。
接下来后手的操作就是找到一个字典序最大的拓扑序。
不难发现先手的最优策略显然是把每个连通块最小的点作为起点,然后每次找到最小且没走过的点走过去。
后手就是每次找入度为0且的点中最大的,然后把这个点删掉。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2005;
int n,a[N],last[N],deg[N],cnt;
bool vis[N];
struct edge{int to,next;}e[N*N];
int gcd(int x,int y)
{
if (!y) return x;
else return gcd(y,x%y);
}
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
deg[v]++;
}
void dfs(int x)
{
vis[x]=1;
for (int i=1;i<=n;i++)
if (!vis[i]&&gcd(a[x],a[i])>1)
{
addedge(x,i);
dfs(i);
}
}
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++) if (!vis[i]) dfs(i);
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++)
{
for (int j=n;j>=1;j--)
if (!vis[j]&&!deg[j])
{
vis[j]=1;
printf("%d ",a[j]);
for (int k=last[j];k;k=e[k].next) deg[e[k].to]--;
break;
}
}
return 0;
}