[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 }