正题
题目大意
一个目前序列,一个目标序列,每次可以选择一个区间交换区间最大值和最小值。
询问在345678345678345678步内将目前序列转换回目标序列的方案(输出该方案)。
解题思路
我们考虑归并排序,对于两个升序的序列,我们考虑如何合并为一个序列。
我们从中间往两边扩张,我们发现在我们可以找到一个kkk使得任意x≤kx\leq kx≤k有amid−k>amid+k+1a_{mid-k}>a_{mid+k+1}amid−k>amid+k+1
我们找到最大的kkk并让i=mid−k,j=mid+k+1i=mid-k,j=mid+k+1i=mid−k,j=mid+k+1这时我们就有这样的
我们就有这样的一个序列,我们考虑将i∼Midi\sim Midi∼Mid和Mid+1∼jMid+1\sim jMid+1∼j翻转(显然可以做到)
然后再将i∼ji\sim ji∼j这段进行翻转就有
然后这个时候必定有L∼iL\sim iL∼i中任意一个数都小于等于j∼Rj\sim Rj∼R中的数。
所以这个时候我们在将L∼MidL\sim MidL∼Mid以i−1i-1i−1为新的MidMidMid再次进行新的排序,然后Mid∼RMid\sim RMid∼R以jjj为新MidMidMid再次进行排序即可。
这时我们发现对于一次排序需要的次数约为nlog2n2\frac{n\log^2 n}{2}2nlog2n,可以通过本题。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5000;
int n,a[N],x[N<<7],y[N<<7],tot,cnt;
void solve(int l,int mid,int r)
{if(mid<l||mid>=r||l==r) return;int i=mid,j=mid+1;while(i>l&&j<r&&a[i-1]>a[j+1]) i--,j++;if(i[a]>j[a]){for(int k=i,p=mid;k<p;k++,p--)swap(k[a],p[a]),x[++tot]=k,y[tot]=p;for(int k=mid+1,p=j;k<p;k++,p--)swap(k[a],p[a]),x[++tot]=k,y[tot]=p;for(int k=i,p=j;k<p;k++,p--)swap(k[a],p[a]),x[++tot]=k,y[tot]=p;solve(l,i-1,mid);solve(mid+1,j,r);}return;
}
void Merge(int l,int r)
{if(l==r) return;int mid=(l+r)/2;Merge(l,mid);Merge(mid+1,r);solve(l,mid,r);return;
}
int main()
{freopen("swap.in","r",stdin);freopen("swap.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&i[a]);Merge(1,n);cnt=tot;for(int i=1;i<=n;i++)scanf("%d",&i[a]);Merge(1,n);printf("%d\n",tot);for(int i=1;i<=cnt;i++)printf("%d %d\n",x[i],y[i]);for(int i=tot;i>cnt;i--)printf("%d %d\n",x[i],y[i]);
}