http://codeforces.com/contest/631/problem/C
题意:给你一个n个数的序列,m次操作
每次操作 两种情况
1 r 表示把 1到r按升序排序
2 r 表示把1到r按降序排序
n,m<=2e5。。。。
显然直接模拟是超时的啦,要找到每次操作之后会产生什么影响..看这些影响能否滞后,
先把操作按r从大到小排序,
如果最大的R为r_max,则把1-r_max存起来并升序排序,用头指针尾指针分别指向头尾 【tmp数组】
对于Ri,其出现的时间为ti,那么对于所有r<Ri,并且t<ti的操作,都可以当作没发生过(会被第i次操作覆盖)
因此,按r排序之后,从大到小遍历所有操作(r递减),如果当前操作的发生时间比上一次有效操作时间早,则当前操作无效,continue; 反之,则当前操作为有效操作,那么看当前的ri,与上一次有效操作的R相比,
如果ri==R,则只需要把 【上一次有效操作】的类型更正为当前操作的类型,
如果ri<R,则 此后的操作再也不能影响到 ri+1到R之间的数,所以ri+1到R之间的数可以被确定下来,如果op==1,升序,他们就是则是1-R的最大X个,反之是最小X个,这时我们只需要移动【tmp数组的头尾指针就可以把最大/最小的X个数放在ri+1到R这个区间】 然后记得更新 【上一次有效操作的发生时间,r的大小,操作类型】
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const double pi=acos(-1.0);
double eps=0.000001;
int tm[200005];
int n;
struct node
{
int op,r;
int id;
};
node vis[200005];
bool cmp(node a,node b)
{return a.r<b.r;}
bool cmp2(int a,int b)
{return a>b;}
int sta[200005];
int main()
{
int n,m ;
int i,j;
cin>>n>>m ;
int pp,x;
for (i=1;i<=n;i++)
scanf("%d",&tm[i]);
for (i=1;i<=m;i++)
{
scanf("%d%d",&vis[i].op,&vis[i].r);
vis[i].id=i;
}
sort(vis+1,vis+1+m,cmp); //按r排序,升序
int rr=vis[m].r; //rr是上一次sort的右端点
int when=vis[m].id; //上一次指令的时间
int kind=vis[m].op; //降序升序
int ok=0;
for (i=1;i<=rr;i++)
{
sta[++ok]=tm[i];
}
sort(sta+1,sta+1+ok); //把1-rr记录下来,并排序,
int hd=1;
int ed=ok;
vis[0].id=m+1;
vis[0].r=0;
for (i=m-1;i>=0;i--) //遍历按r递减的操作
{
int r =vis[i].r;
int pp=vis[i].op;
int t=vis[i].id;
if (t<when) continue; //如果r<=rr,并且还在rr指令的前面,则该指令无效
else
{
if (r==rr) //如果r==rr,并且t>when,则可以覆盖升序降序类型
{
kind=pp;
when=t;
//rr=r;
}
else //如果r指令较晚,并且r<rr,则r+1到rr之间的数可以确定,如果rr是升序,则是1-rr的最大X个,否则是最小X个
{
for (j=rr;j>=r+1;j--)
{
if (kind==1)
{
tm[j]=sta[ed];ed--;
}
else
{
tm[j]=sta[hd];hd++;
}
}
rr=r;
kind=pp;
when=t;
}
}
}
for (i=1;i<=n;i++)
{
if (i!=1) printf(" ");
printf("%d",tm[i]);
}
printf("\n");
return 0;
}