cf#344-C - Report-贪心/单调栈

来源:互联网 发布:mac os x 原版dmg 编辑:程序博客网 时间:2024/05/22 00:38

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;} 



0 0