[NC记录]ARC110F Esoswap

command_block

2021-10-27 08:07:32

Personal

**题意** : 给出一个 $0\sim n-1$ 的排列 $P_{0\sim n-1}$。 可以使用下列操作至多 $2\times 10^5$ 次: - 选定 $0\leq i<n$ ,交换 $P_i,P_{(i+P_i)\bmod n}$ 。 目标是将 $P$ 排序,构造一种方案或指出无解。 $n\leq 100$ ,时限$\texttt{2s}$。 ------------ 构造题,先尝试观察一些特化方案。 若不断操作 $i$ ,记初始时 $P_i=a$ ,可以发现,若要把 $a$ 拿回来除非 $P_i$ 再次为 $a$ ,可 $a$ 已经走了。 所以,重复执行操作必然会得到不同的数,直到得到 $0$ 为止。 首先不断执行 $n-1$ ,将 $0$ 移到 $n-1$。 然后依次不断执行 $n-2$ ,拿到 $1$ 为止;执行 $n-3$ ,拿到 $2$ 为止…… 可以发现,在拿 $i$ 时,$0\sim i-1$ 都已经被固定,可能跳过的只有 $i+1\sim n$ ,而这些数不会影响到后面的 $0\sim i-1$ 。 于是我们用 $O(n^2)$ 次操作使得序列降序。 考虑逐渐对后缀排序,假设 $0\sim i-1$ 已经排好序,接下来要 插入 $i$。 此时对 $i$ 操作,恰可以将 $i$ 与 $P_n$ 交换。 注意到 $1$ 可以任意移动,我们将 $1$ 移到 $n$ ,然后操作 $i$ ,然后将 $1$ 移回去即可。 ```cpp #include<algorithm> #include<cstdio> #define MaxN 105 #define MaxS 200500 using namespace std; int n,tn,p[MaxN],ans[MaxS]; void mdf(int i){ ans[++tn]=i; swap(p[i],p[(i+p[i])%n]); } int main() { scanf("%d",&n); for (int i=0;i<n;i++)scanf("%d",&p[i]); for (int i=n-1;i;i--)while(p[i]!=n-i-1)mdf(i); mdf(n-2); for (int i=n-3;i>=0;i--){ for (int j=i+2;j<n-1;j++)mdf(j); mdf(i);mdf(i); } printf("%d\n",tn); for (int i=1;i<=tn;i++)printf("%d\n",ans[i]); return 0; } ```