题目分析
以123456789为例
如果我们将第4位之前的元素降序排列
432156789
然后对任何位置后的元素升序排列都不会影响到最后的56789
如果接下来的若干次操作都没有使得>=4的位置n前面的元素降序排列,那么对n之前的元素降序排列将会撤销之前的所有操作,设n=6我们会得到
654321789
同理设i>=m,对小于m的元素降序排列将撤回许多操作,直到遇到操作"对i前的元素降序排列"。
还可以发现对于较大的n操作"将n前的元素降序排列"将会吸收若干连读的"将m前的元素降序排列"操作,其中m<=n
将m右侧的元素升序排列也有类似的性质
最终剩下的操作一定是交替出现的,n严格递减,m严格递曾的“对n前的元素降序排列”操作与“对m后的元素升序排列”操作
对于这类操作,每次将操作共同影响的区间反向就行了。
比如n,m依次为(操作时包括第m,n个元素)
9 1
8 2
7 3
对123456789是
9 1
987654321
123456789
8 2
876543219
812345679
7 3
865432179
861234579
…
算法实现
细节较多,讲不清楚,建议在纸上自己画画
代码中有详细注释
#include<bits/stdc++.h>
#define NMAX 100001 //最后多一个元素用来取地址给reverse用
//n:new
#define nd 0 //新元素类型为左递减
#define nu 1 //新元素类型为右递增
//d:down,u:up
int d[NMAX+1]={NMAX};//左递减操作序列,用大值托底(但这不算一次操作)
int u[NMAX+1]={0,1};//右递增操作序列,用小值托底(这个算一次操作)
//sd:sizeofDown
int sd=0,su=1;//左递减操作序列、右递增操作序列的大小,认为右递增序列中有一个元素1,因为序列最初是从1开始右递增的,可认为是第一次操作的结果
int N,T;//数字序列长度,操作次数
int A[NMAX+1];//保存数字序列
using namespace std;
int main(){
cin >> N >> T;
//v:value
int type,v;
while(T--){
cin >> type >> v;
if(type==nd){//如果是左递减操作
if(v>=d[sd]){//撤回无用操作
while(v>=d[sd])sd--;
++sd,su=sd,d[sd]=v;
}else if(sd<su){//不能撤回就添加新操作(连续较小值的同种操作被sd<su吸收了)
++sd,d[sd]=v;
}
}else{
if(v<=u[su]){//同上
while(v<=u[su])su--;
++su,sd=su-1,u[su]=v;
}else if(sd==su){
++su,u[su]=v;
}
}
}
for(int i=1;i<=N;++i){//初始值
A[i]=i;
}
//p:pointer,u:up,d:down
int pu=1;//现在从右递减的第一步开始执行操作
int pd=1;
while(pu<su){
if(u[pu]<d[pd])//之前没考虑过m,n相撞的情况,在这里要避免
reverse(&A[u[pu]],&A[d[pd]+1]);
++pu;
if(u[pu]<d[pd])
reverse(&A[u[pu]],&A[d[pd]+1]);
++pd;
}
if(su==sd){//如果两个大小一样则还差一步操作
if(u[pu]<d[pd])
reverse(&A[u[pu]],&A[d[pd]+1]);
}
for(int i=1;i<=N;++i){
cout << A[i] << ' ';
}
cout << endl;
return 0;
}