题解 P7999 [WFOI - 01] 翻转序列(requese)
题意
你有一个长度为
请在
部分分
对于
假设我们已经将
每次,我们可以翻转长
次数约为
正解
我们从
处理
假设最后
如果
否则,我们可以操作
但上述操作对剩余位置的长度有要求,注意到
因此,我们取
方便起见,我们先用类似的操作处理序列的后半部分,使
然后处理前半部分,然而虽然序列长度足够,但是后半部分已经处理好,不能够随意翻转。
注意到我们倒着做操作可以复原序列,运用这个性质,我们尝试交换
一个至关重要的操作是同时使用
由于我们的操作
复原一个数的次数上界是
代码实现时对
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int n,a[N],x,y;
vector<pair<int,int> > op;
inline void oper(int u,int v){
reverse(a+u,a+v+1);
op.push_back(make_pair(u,v));
}
inline void prt(){
for(int i=1;i<=n;i++) if(a[i]!=i){
puts("wrong operations !!");
exit(-1);
}
printf("%d\n",(int)op.size());
for(auto u:op) printf("%d %d\n",u.first,u.second);
}
inline void force(){
puts("3");
for(int u=1,v;u<n;u++){
for(v=u;v<=n;v++) if(a[v]==u) break;
while((v-u)%3) oper(v-1,v),v--;
while(v>u) oper(v-3,v),v-=3;
}
prt();
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(n<=4) force(),exit(0);
x=n>>2;
if(!(x&1)) x--;
printf("%d\n",x);
y=(x+1)>>1;
//后半部分
for(int u=n,v;u>(n/2);u--){
for(v=u;v;v--) if(a[v]==u) break;
while(v+x<=u) oper(v,v+x),v+=x;
if(v==u) continue;
int l=u,r=u-x;
if((l-v)&1) oper(v-x,v),v-=x;//同下
oper((v+r)/2-y+1,(v+r)/2+y);
oper(r,r+x);
}
//前半部分
for(int u=1,v,rv;u<=(n/2);u++){
for(v=u;v<=n;v++) if(a[v]==u) break;
while(v-x>=u) oper(v-x,v),v-=x;
if(v==u) continue;
int l=u,r=u+x;rv=0;
if((l-v)&1) oper(v,v+x),v+=x,rv=1;//调整奇偶性
oper((v+r)/2-y+1,(v+r)/2+y);//将 u 放到位置 u+x 上
oper(l,l+x);oper(l+1,l+x-1);//交换 u 和 u+x 上的值
oper((v+r)/2-y+1,(v+r)/2+y);//复原序列
if(rv) oper(v-x,v);//复原序列
}
prt();
}