[图论记录]AT2306 [AGC010E] Rearranging
command_block
2021-04-28 09:21:22
**题意** : 给出一个长度为 $n$ 的序列 $a$。
玩家 A 会将序列 $a$ 任意打乱,然后玩家 B 进行操作,每次可以将两个**相邻**且**互质**的数交换。
玩家 A 希望最终序列的字典序尽量小,而玩家 B 希望最终序列的字典序尽量大,问最终序列。
$n\leq 2000$ ,时限$\texttt{2s}$。
------------
咋这一场 DEF 都是博弈……
先考虑在给定序列后玩家 B 会如何操作。
对于那些不互质的数,操作后相对顺序不会改变。不难发现,这也是方案合法的充要条件。
于是,对于每一组不互质的对子 $a_i,a_j$ ,连边 $i\rightarrow j$ ,表示 $i$ 一定要在 $j$ 前面。
现在求该 $\rm DAG$ 的最大拓扑序(列)。
根据“字典拓扑原理”,用堆贪心地进行拓扑排序,每次取点权最大的点扩展即可。
然后,考虑 A 的对策。
对于每一组不互质的对子 $a_i,a_j$ ,连无向边。先手要将无向边定向,使得形成的 $\rm DAG$ 的最大拓扑序最小。
对于每个联通块,我们令权值最小的点 $t$(若有多个,则任选一个)为唯一零度点,开始向外定向。
这样能保证第一个点一定是权值最小的点。反之,若有多个零度点,则第一个点可以不是权值最小的点,更劣。
在定向时,从 $t$ 开始 $\rm dfs$ ,优先前往权值较小的儿子。
将 $\rm dfs$ 树上的边按照从上到下的顺序定向,$\rm dfs$ 的返祖边也从上到下(这些边是无用的,丢掉也行)。
若不优先前往权值较小的儿子,可能该分支与其他分支联通,造成这个儿子访问顺序必晚于某个更大的数,更劣。
若不连通,则先走谁没关系。“优先前往权值较小的儿子”这一策略能保证每个联通分量先被遍历到可能的最小点,故是最优的,
确定了图之后,跑 B 的贪心即可。
复杂度 $O(n^2\log a)$。
```cpp
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#define Pr pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define MaxN 2050
using namespace std;
int gcd(int a,int b)
{return !b ? a : gcd(b,a%b);}
vector<int> g[MaxN],g2[MaxN];
int rt,a[MaxN],d[MaxN],vis[MaxN];
void adl(int u,int v)
{g2[u].pb(v);d[v]++;}
void dfs1(int u)
{
if (a[u]<a[rt])rt=u;
vis[u]=1;
for (int i=0;i<g[u].size();i++)
if (!vis[g[u][i]])dfs1(g[u][i]);
}
void dfs2(int u)
{
vis[u]=2;
for (int i=0;i<g[u].size();i++)
if (vis[g[u][i]]!=2){
adl(u,g[u][i]);
dfs2(g[u][i]);
}
}
int n,ans[MaxN],tn;
priority_queue<Pr> q;
bool cmp(int A,int B){return a[A]<a[B];}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
a[0]=100000001;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (gcd(a[i],a[j])>1)
{g[i].pb(j);g[j].pb(i);}
for (int i=1;i<=n;i++)
sort(g[i].begin(),g[i].end(),cmp);
for (int i=1;i<=n;i++)
if (!vis[i]){
rt=0;dfs1(i);
dfs2(rt);
}
for (int i=1;i<=n;i++)
if (!d[i])q.push(mp(a[i],i));
while(!q.empty()){
Pr now=q.top();q.pop();
ans[++tn]=now.fir;
int u=now.sec;
for (int i=0;i<g2[u].size();i++)
if (!--d[g2[u][i]])q.push(mp(a[g2[u][i]],g2[u][i]));
}
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}
```