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

70 篇文章 0 订阅
12 篇文章 0 订阅

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值