题意:给一个1~n排列,1<=n<=10000,每次操作选取一个长度为偶数的连续区间。交换前一半和后一半,使它变成升序。
题解:每次只要把最小的移动到最左边,那么问题规模就缩小了。假设当前区间为[l,r],不难发现,只要最小的数字在[l,l+(r+1-l)/2]这个区间内,一定可以通过一次交换把最小的数字移动到l处,否则,先一定可以一次交换把最小的数字移动到上述区间中。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4+5; int a[maxn],pos[maxn]; inline void exchange(int s1,int s2) { int len = s2 -s1; for(int i = s1, maxi = s1+len; i < maxi; i++){ swap(pos[a[i]],pos[a[i+len]]); swap(a[i],a[i+len]); } } int L[maxn<<1],R[maxn<<1]; #define OUT(a,b) printf("%d %d ",a,b) #define ADDANS(l,r) L[cnt] = l;R[cnt++] = r int main() { //freopen("in.txt","r",stdin); int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); for(int i = 1; i <= n; i++){ scanf("%d",a+i); pos[a[i]] = i; } int cnt = 0; for(int i = 1,tmp = n+1; i < n; i++ )if(pos[i]!=i) { if( pos[i]<<1 > tmp+i ){ int len = tmp - i; if(len&1){ ADDANS(i+1,n); exchange(i+1,i+1+(len>>1)); }else { ADDANS(i,n); exchange(i,i+(len>>1)); } } ADDANS(i,(pos[i]<<1)-i-1); exchange(i,pos[i]); } printf("%d ",cnt); for(int i = 0; i < cnt; i++){ OUT(L[i],R[i]); } } return 0; }