[atARC110F]Esoswap

先构造使得$p_{i}$降序(即$p_{i}=n-1-i$),只需要从后往前,不断执行$i$操作直至合法即可

正确性的证明:首先保证了$[0,n-i)$这些数字都已经出现,因此操作不会破坏已确定的数字的顺序

同时,一个数字不会重复出现,因为若要与其交换后的位置再交换,仍需要其本身,显然不可能

(特别的,对于0由于已经会在$n-1$上出现,因此不需要考虑)

由于每一个位置上每一个数至多出现1次,因此至多$n^{2}$次操作即可

再考虑如何把顺序调回来,先将一个后缀排好,然后考虑插入一个数$i$(后缀必然是$[0,i)$)

考虑操作$p_{j}=1$的位置$j$,相当于将$j$向后挪1步,因此可以将1挪到末尾,又因为$i+p_{i}=n-1$,执行操作$i$将其与1交换,再将1与0交换即可

例如:540123->540213->540231(把1挪到末尾)->510234(执行操作$i$)->501234(将1与0交换)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 105
 4 vector<int>ans;
 5 int n,a[N];
 6 void write(int k){
 7     ans.push_back(k);
 8     swap(a[k],a[(k+a[k])%n]);
 9 }
10 int main(){
11     scanf("%d",&n);
12     for(int i=0;i<n;i++)scanf("%d",&a[i]);
13     for(int i=n-1;i;i--)
14         while (a[i]!=n-i-1)write(i);
15     for(int i=n-2;i>=0;i--){
16         for(int j=i+1;j<n-1;j++)write(j);
17         write(i);
18         write(i);
19     }
20     for(int i=0;i<n;i++)assert(a[i]==i);
21     printf("%d\n",ans.size());
22     for(int i=0;i<ans.size();i++)printf("%d\n",ans[i]);
23 } 
View Code

 

posted @ 2020-12-10 13:21  PYWBKTDA  阅读(277)  评论(0编辑  收藏  举报