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

题目链接

题意:给你一个初始状态为空的栈和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
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&&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

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

#include
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=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

 

你可能感兴趣的:(RMQ,线段树,分块,差分)