[SCOI 2015集训-2015.4.16]Problem A(离散化+线段树)

题目描述

在如今的网络中,TCP 是一种被广泛使用的网络协议,它在传输层提供了可靠的通信服务。
众所周知,网络是存在时延的,例如用户先后向服务器发送了两个指令 op1 op2 ,并且希
望服务器先处理指令 op1 ,再处理指令 op2 ;但由于网络时延,这两个指令可能会失序到达,
而导致服务器先执行了指令 op2 ,这是我们不希望看到的。TCP 协议拥有将失序到达的报文
按顺序重组的功能,一种方法是给每一个报文打上一个时间戳。而你今天要实现的功能比这
个要简单很多。我们需要你维护一个服务器,这个服务器的功能是一个简单的栈,你会接收
三种用户的指令:
push x t — 表示将 x 元素入栈,这条指令的时间戳为 t
pop t — 表示将栈顶元素弹出,这条指令的时间戳为 t
peak t — 用户询问现在栈顶元素的值,这条指令的时间戳为 t
当一条时间戳为 t 的指令到达时,你需要进行如下处理:
1.将所有之前执行的时间戳大于 t push pop 指令全部撤销
2.执行当前这条指令
3.按时间戳顺序重新执行在第 1 步被撤销的指令
注意你不需要撤销以及重新执行之前已经执行过的 peak 指令,也就是说每一条 peak 指令只
有在它到达的时候会被执行一次。
我们保证每一条指令的时间戳都是唯一的;若你在需要执行一条 pop 指令时发现当前栈为
空,则当前你可以忽略这条指令。

输入

第一行包含一个整数 n ,表示指令总数。
接下来 n 行按指令到达服务器的顺序给出每一条指令,有三种类型
push x t
pop t
peak t

输出

对于每一条 peak指令,输出对应的答案占一行;若栈为空,输出 1
样例输入:
7
push 100 3
push 200 7
peak 4
push 50 2
pop 5
peak 6
peak 8

样例输出

100
50
200

数据范围

对于 10%的数据,1 <= n <= 1000;
对于 40%的数据,1 <= n <= 80000;
对于 100%的数据,1 <= n <= 300000,0 <= xt <= 1000000000。

思路

虽然题目非常绕口,但是可以归纳为:在查询时间 t 的栈顶元素时,从头开始离线对所有时刻小于等于t的所有操作全部进行一遍。

那么我们可以先对所有的 t 离散化以减少空间消耗,然后维护一个线段树,线段树上的序列下标对应的就是时间点,线段树上某个位置t的值为1,表示此刻有个元素入栈,并标记这个时刻入栈的是什么元素,-1表示此刻进行了一次出栈操作。因为题目要求,因此对于 i>j 的两个操作 i,j ,若它们都是在同一时刻,那么用新的 i 覆盖序列中旧的j如果维护一个值 x 代表当前时刻t栈中元素个数的话,初始 x 为0,从1到t对-1 0 1序列扫一遍,若某时 x=0 则不能做-1,否则加上序列的当前位置的元素。这样就能得到 x ,而且也能想到,时刻t时栈顶的元素是最大的 i<t ,使得-1 0 1的序列上的区间 [i,t] 中区间和 >0

容易想到在线段树上维护区间和标记 sum[] ,一个比较好做的做法就是二分 i ,每次对区间求和然后修改上下界,这样的做法是O(qlog2n)的,由于此题很坑比地卡 log ,因此是不能用这种做法的。

然后考虑更快的做法:维护线段树上的区间内最大后缀和标记 maxright ,将 [1,t] 这段区间,拆分成尽量少的线段树结点,然后从最右边那个结点开始暴力扫,维护当前扫过的结点对应区间和 sum ,若扫到结点 j ,且maxright[j]+sum>0,则说明我们要找的 i 就在结点j对应的区间中,再次在这个结点对应于线段树的子树中二分查找即可。

评测结果

这里写图片描述

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 310000
#define lson (o<<1)
#define rson (o<<1|1)
#define INF 0x3f3f3f3f

using namespace std;

//num[i]=第i次操作入栈的元素(假如第i次操作是入栈的话),val[i]=时刻i入栈的元素(假如时刻i存在入栈操作) 
int cmd[MAXN],t[MAXN],num[MAXN],data[MAXN],val[MAXN]; //t[i]=离散化后第i次操作的时间 
int sum_tag[MAXN<<2],maxright_tag[MAXN<<2]; //sum_tag[o]=节点o对应区间和,maxright_tag[o]=节点o中最大的后缀和 

void pushup(int o)
{
    sum_tag[o]=sum_tag[lson]+sum_tag[rson];
    maxright_tag[o]=max(maxright_tag[lson]+sum_tag[rson],maxright_tag[rson]);
}

void update(int o,int L,int R,int pos,int val)
{
    if(L==R)
    {
        sum_tag[o]=maxright_tag[o]=val;
        return;
    }
    int M=(L+R)>>1;
    if(pos<=M) update(lson,L,M,pos,val);
    else update(rson,M+1,R,pos,val);
    pushup(o);
}

int cnt=0;

struct Interval
{
    int L,R,id;
    Interval(){}
    Interval(int _L,int _R,int _id):L(_L),R(_R),id(_id){}
}intervals[MAXN];

void getseg(int o,int L,int R,int ql,int qr)
{
    if(ql==L&&qr==R)
    {
        intervals[++cnt]=Interval(L,R,o);
        return;
    }
    int M=(L+R)>>1;
    if(qr<=M) getseg(lson,L,M,ql,qr);
    else if(ql>M) getseg(rson,M+1,R,ql,qr);
    else
    {
        getseg(lson,L,M,ql,M);
        getseg(rson,M+1,R,M+1,qr);
    }
}

int BinarySearch(int o,int L,int R,int sum) //在[L,R]的节点o中找最靠右边的i,使得后缀[i,tot]中的元素和>0,(R,tot]这段区间内的元素和已知为sum 
{
    while(L!=R)
    {
        int M=(L+R)>>1;
        if(sum+maxright_tag[rson]>0) o=rson,L=M+1;
        else
        {
            sum+=sum_tag[rson];
            o=lson;
            R=M;
        }
    }
    return L;
}

int main()
{
    freopen("A.in","r",stdin);
    freopen("A.out","w",stdout); 
    char s[10];
    int q,n=0,tot=0;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%s",s);
        if(s[1]=='u') //1:push
        {
            cmd[i]=1;
            scanf("%d%d",&num[i],&t[i]);
        }
        else if(s[1]=='o') //2:pop
        {
            cmd[i]=2;
            scanf("%d",&t[i]);
        }
        else //3:peak
        {
            cmd[i]=3;
            scanf("%d",&t[i]);
        }
        data[tot++]=t[i];
    }
    sort(data,data+tot); //时间离散化
    for(int i=1;i<=q;i++) t[i]=1+lower_bound(data,data+tot,t[i])-data; 
    for(int i=1;i<=q;i++)
    {
        if(cmd[i]==1) //1:push
        {
            update(1,1,tot,t[i],1);
            val[t[i]]=num[i];
        }
        else if(cmd[i]==2) //2:pop
            update(1,1,tot,t[i],-1);
        else
        {
            cnt=0;
            bool flag=false;
            getseg(1,1,tot,1,t[i]);
            int sum=0; //sum=当前的后缀和 
            for(int j=cnt;j>=1;j--)
            {
                if(sum+maxright_tag[intervals[j].id]>0)
                {
                    printf("%d\n",val[BinarySearch(intervals[j].id,intervals[j].L,intervals[j].R,sum)]);
                    flag=true;
                    break;
                }
                sum+=sum_tag[intervals[j].id]; //!!!!!
            }
            if(!flag) printf("-1\n");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值