HDU - 4967 Handling the Past (线段树/分块)

版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/83108051

题目链接

题意:给你一个初始状态为空的栈和n个操作,操作共有三种:

1.向栈中加入一个元素(push)

2.清除栈顶元素(pop)

3.询问栈顶元素(peak)

每个操作都有一个时间戳,在执行每个操作之前,都需要把所有时间戳在该操作之前的操作全都撤销,然后执行该操作,并重做之前的操作(peak不需要重做)。

解法:构造一棵线段树,每个区间需要维护两个值:sum(区间和),maxrsum(区间最大后缀和。首先对所有的时间戳离散化,对于每个push操作,将该操作对应的时间戳的位置上的元素+1,对于pop操作则-1,对于peak操作,从时间戳往前第一个到该时间戳的后缀大于0的时间点就是栈顶元素被push进的时间点。

区间最大后缀和维护公式:maxrsum[o]=max(maxrsum[o<<1]+sum[o<<1|1],maxrsum[o<<1|1])

询问的过程略为复杂,要从后往前枚举每个子区间,对于访问过的区间还要对区间和进行累加,便于前面区间的后缀和计算。

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int INF=0x3f3f3f3f;
struct query
{
    int type,num,t;
} qr[N];
int n,order[N],val[N],tt,kase=0;
struct SEGTREE
{
    static const int N=2e5+10;
    int sum[N],maxrsum[N];
    void init()
    {
        memset(sum,0,sizeof sum);
        memset(maxrsum,0,sizeof maxrsum);
    }
    void add(int pos,int x,int o=1,int l=0,int r=n-1)
    {
        if(l==r)
        {
            sum[o]+=x;
            maxrsum[o]=x;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)add(pos,x,o<<1,l,mid);
        else add(pos,x,o<<1|1,mid+1,r);
        sum[o]=sum[o<<1]+sum[o<<1|1];
        maxrsum[o]=max(maxrsum[o<<1]+sum[o<<1|1],maxrsum[o<<1|1]);
    }
    int askans(int L,int R,int o=1,int l=0,int r=n-1)
    {
        if(l>R||r<L)return -1;
        if(l>=L&&r<=R&&maxrsum[o]+tt<=0)
        {
            tt+=sum[o];
            return -1;
        }
        if(l==r)return l;
        int t;
        int mid=(l+r)>>1;
        if(~(t=askans(L,R,o<<1|1,mid+1,r)))return t;
        return askans(L,R,o<<1,l,mid);
    }
} segtree;

int main()
{
    while(scanf("%d",&n)&&n)
    {
        printf("Case #%d:\n",++kase);
        segtree.init();
        char buf[10];
        for(int i=0; i<n; ++i)
        {
            scanf("%s",buf);
            if(buf[1]=='u')
            {
                qr[i].type=1;
                scanf("%d%d",&qr[i].num,&qr[i].t);
            }
            else if(buf[1]=='o')
            {
                qr[i].type=2;
                scanf("%d",&qr[i].t);
            }
            else if(buf[1]=='e')
            {
                qr[i].type=3;
                scanf("%d",&qr[i].t);
            }
            order[i]=qr[i].t;
        }
        sort(order,order+n);
        for(int i=0; i<n; ++i)qr[i].t=lower_bound(order,order+n,qr[i].t)-order;
        for(int i=0; i<n; ++i)
        {
            if(qr[i].type==1)
            {
                segtree.add(qr[i].t,1);
                val[qr[i].t]=qr[i].num;
            }
            else if(qr[i].type==2)
            {
                segtree.add(qr[i].t,-1);
            }
            else if(qr[i].type==3)
            {
                tt=0;
                int ans=segtree.askans(0,qr[i].t);
                printf("%d\n",~ans?val[ans]:-1);
            }
        }
    }
    return 0;
}

如果感觉线段树的方法不大好想,也可以考虑采用分块的方法,只比线段树的方法慢了400ms:

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int INF=0x3f3f3f3f;
struct query
{
    int type,num,t;
} qr[N];
int n,order[N],val[N],tt,kase=0;

struct BLOCK
{
    static const int N=5e4+10;
    static const int SQRTN=sqrt(N)+10;
    int rsum[N],sum[SQRTN],maxrsum[SQRTN],in[N];
    void init()
    {
        memset(sum,0,sizeof sum);
        memset(rsum,0,sizeof rsum);
        memset(maxrsum,0,sizeof maxrsum);
    }
    void getin()
    {
        int sz=sqrt(n+0.5);
        for(int i=0; i<n; ++i)in[i]=i/sz;
    }
    void add(int pos,int x)
    {
        sum[in[pos]]+=x;
        maxrsum[in[pos]]=~INF;
        int l=lower_bound(in,in+n,in[pos])-in;
        int r=upper_bound(in,in+n,in[pos])-in-1;
        for(int i=l; i<=pos; ++i)rsum[i]+=x;
        for(int i=l; i<=r; ++i)maxrsum[in[i]]=max(maxrsum[in[i]],rsum[i]);
    }
    int askans(int R)
    {
        int l=lower_bound(in,in+n,in[R])-in;
        int r=upper_bound(in,in+n,in[R])-in-1;
        int tmp=r==R?0:rsum[R+1];
        for(int i=R; i>=l; --i)if(rsum[i]-tmp>0)return i;
        tmp=sum[in[R]]-tmp;
        for(int u=in[R]-1; u>=0; --u)
        {
            if(maxrsum[u]+tmp<=0)
            {
                tmp+=sum[u];
                continue;
            }
            l=lower_bound(in,in+n,u)-in;
            r=upper_bound(in,in+n,u)-in-1;
            for(int i=r; i>=l; --i)if(rsum[i]+tmp>0)return i;
        }
        return -1;
    }
} block;

int main()
{
    while(scanf("%d",&n)&&n)
    {
        printf("Case #%d:\n",++kase);
        block.init();
        block.getin();
        char buf[10];
        for(int i=0; i<n; ++i)
        {
            scanf("%s",buf);
            if(buf[1]=='u')
            {
                qr[i].type=1;
                scanf("%d%d",&qr[i].num,&qr[i].t);
            }
            else if(buf[1]=='o')
            {
                qr[i].type=2;
                scanf("%d",&qr[i].t);
            }
            else if(buf[1]=='e')
            {
                qr[i].type=3;
                scanf("%d",&qr[i].t);
            }
            order[i]=qr[i].t;
        }
        sort(order,order+n);
        for(int i=0; i<n; ++i)qr[i].t=lower_bound(order,order+n,qr[i].t)-order;
        for(int i=0; i<n; ++i)
        {
            if(qr[i].type==1)
            {
                block.add(qr[i].t,1);
                val[qr[i].t]=qr[i].num;
            }
            else if(qr[i].type==2)
            {
                block.add(qr[i].t,-1);
            }
            else if(qr[i].type==3)
            {
                tt=0;
                int ans=block.askans(qr[i].t);
                printf("%d\n",~ans?val[ans]:-1);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/83108051