ROI 2018 C
Day2 C Quick Sort
题目大意: 定义对序列 \(\{a_i\}\) 上一段区间 \([l,r]\) 的操作为 提出区间,奇偶分别合并放到两边 。
给出排列 \(\{a_i\}\) ,求将 \(\{a_i\}\) 排序的方案,要求操作数 \(\le 15000\) 。
长度 \(n \le 3000\) 。
题解:
一个显然的想法是从小到大处理每个 \(i\) ,设 \(p\) 为 \(i\) 当前的位置,则操作 \([i,p]\) 或 \((i,p]\) ,能够得到 \(80\) pts。
发现此过程每一步都需要几乎卡满 \(\log\) ,原因是操作后 \(i\) 只会变到 \(\frac{i+p}{2}\) 的位置。
考虑该过程倒过来,此时操作的结果是 中间的被分到两侧 ,这样就降低了局限性。
从大往小做,每次 \(p \rightarrow 2p\) ,分析可知期望操作数是 \(O(n)\) 的。
为了放置被卡,需要一开始随机打乱,即随机若干个区间操作。
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
const int N=3010;
int n,a[N],t[N],b[N];
vector<pair<int,int>> ans,q;
void push(int l,int r){
ans.push_back(make_pair(l,r));
fo(i,l,r)b[i]=a[i];
int mid=l+r-1>>1;
fo(i,l,mid)a[l+(i-l)*2+1]=b[i];
fo(i,mid+1,r)a[l+(i-mid)*2-2]=b[i];
fo(i,l,r)t[a[i]]=i;
}
void solve(int x){
int cnt=0;
for(int p=t[x];p<x;p=t[x]){
if((p<<1)<=x)push(1,p<<1);
else push(p*2-x+1,x);
++cnt;
// if(cnt==100)
// puts("DEBUG");
}
}
void output(){
printf("%d\n",ans.size());
reverse(ans.begin(),ans.end());
for(auto it:ans)printf("%d %d\n",it.first,it.second);
}
int main(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
// printf("%d\n",3000);
// fd(i,3000,1)printf("%d ",i);
// printf("\n");
// return 0;
srand(time(NULL));
scanf("%d",&n);
fo(i,1,n){
int x;
scanf("%d",&x);
a[x]=i;t[i]=x;
}
fo(i,1,100){
int l=rand()%n+1,r=rand()%n+1;
if(l>r)swap(l,r);
push(l,r);
}
fd(i,n,1)
solve(i);
output();
return 0;
}