题目:
对n<200000的数组,给出m种从1开始到r为止的升序或降序排序,m<200000,求最终输出序列
题解:
1. 不管前面排了多少次小区间,只要在后面排了一次大区间,结果只受最后一次排序影响。所以维护单调队列。
2. 前面是大区间,后面是小区间,只有当排序方式不同时才需要重新排序。
3. 第一次排序过后剩下的排序等价于翻转。
好这样就有了O(N2)的算法,暴力一波看看数据水不水。
……那再优化一下……
翻转不需要真的翻转,每一次更小区间的翻转都会空出最右端的数字,这个数字满足前一次排序的性质。
所以我们还得维护一个表示所需数字的单调队列。
还有一些小细节,自行注意。
#include<iostream>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
struct S{
int t,r;
}s[201000];
int arr[201000];
int ans[201000];
deque<S> deq;//单调递减队列
deque<int> smooth;//单调递增队列
int main(){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>arr[i];
}
for(int i=0;i<m;i++){
cin>>s[i].t>>s[i].r;
}
for(int i=0;i<m;i++){
while(!deq.empty()&&s[i].r>=deq.back().r){
deq.pop_back();
}
deq.push_back(s[i]);
}
S now=deq.front();
sort(arr,arr+now.r);
for(int i=0;i<now.r;i++){
smooth.push_back(arr[i]);
}
if(now.t==1) sort(arr,arr+now.r,less<int>());
else sort(arr,arr+now.r,greater<int>());
deq.pop_front();
for(int i=now.r;i<n;i++) ans[i]=arr[i];
int nex=0;
if(deq.empty()){
for(int i=0;i<n;i++) ans[i]=arr[i];
for(int i=0;i<n;i++){
cout<<ans[i];
if(i==n-1) cout<<endl;
else cout<<' ';
}
return 0;
}
bool flag=false;
while(!deq.empty()){
if(flag){
now=deq.front();
deq.pop_front();
}
flag=true;
if(deq.empty()) nex=0;
else nex=deq.front().r;
if(now.t==1){
for(int i=now.r-1;i>=nex;i--){
ans[i]=smooth.back();
smooth.pop_back();
}
}
else{
for(int i=now.r-1;i>=nex;i--){
ans[i]=smooth.front();
smooth.pop_front();
}
}
}
for(int i=0;i<n;i++){
cout<<ans[i];
if(i==n-1) cout<<endl;
else cout<<' ';
}
}