hdu 4967 Handling the Past 线段树,最右正值区间

18 篇文章 0 订阅
7 篇文章 0 订阅
最后一场多校,又被吊打。
最近学了一些莫名其妙的算法,然而最简单的线段树都写不好。(政神1小时秒杀的题目我改了4小时。)
还是静下心来,学好用好每一种算法。

hdu 4967 Handling the Past 
看了题解。很巧妙的做法。
利用线段树记录一个值 rmax=max{左儿子的rmax+右儿子的sum,右儿子的rmax}
通过这个值可以快速找到 总和为正值的最右位置
需要注意的是,当递归到左儿子时,sum要加上右儿子中在查询区间内的sum。我的程序中用了一个带引用的tsum变量完成了这个求和操作。

然后push就是在位置上+1,pop就在位置上-1,peak时通过rmax找到为正的最右位置输出即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ls (p<<1)
#define rs (p<<1|1)
#define Maxn 50100

int n,a[Maxn],b[Maxn],o[Maxn],pos[Maxn],r[Maxn],tim[Maxn];

int getpos(int j){
    int l=1,r=n,m;
    while(l<=r){
        m=l+r>>1;
        if (tim[m]==j) return m;
        else if (tim[m]>j) r=m-1;
        else l=m+1;
    }
}

struct segtree{int l,r,sum,rmax;}t[Maxn*4];

inline void update(int p){
    if (t[p].l==t[p].r) return;
    t[p].sum=t[ls].sum+t[rs].sum;
    t[p].rmax=max(t[rs].rmax,t[ls].rmax+t[rs].sum);
}

void build(int l,int r,int p){
    t[p].l=l;t[p].r=r;
    t[p].sum=0;t[p].rmax=0;
    if (l==r) return;
    int m=l+r>>1;
    build(l,m,ls);
    build(m+1,r,rs);
}

void insert(int u,int p,int val){
    if (t[p].l==t[p].r){
        t[p].sum+=val;
        t[p].rmax=t[p].sum;
        return;
    }
    int m=t[p].l+t[p].r>>1;
    if (u<=m)insert(u,ls,val);
    else insert(u,rs,val);
    update(p);
}

int query(int l,int r,int p,int sum,int &tsum){
    int m=t[p].l+t[p].r>>1;
    int tsum1=0;
    if (t[p].l==l&&t[p].r==r){
        tsum=t[p].sum;
        if (sum+t[p].rmax<=0) return -1;
        else{
            if (t[p].l==t[p].r) return t[p].l;
            if (sum+t[rs].rmax>0) return query(m+1,r,rs,sum,tsum1);
            else if(sum+t[rs].sum+t[ls].rmax>0) return query(l,m,ls,sum+t[rs].sum,tsum1);
        }
    }
    int ret;
    if (r<=m) {ret=query(l,r,ls,sum,tsum1);tsum=tsum1;}
    else if (l>m) {ret=query(l,r,rs,sum,tsum1);tsum=tsum1;}
    else{
        int tsum2=0,tmp1,tmp2;
        tmp1=query(m+1,r,rs,sum,tsum1);
        tsum2=tsum1;
        tmp2=query(l,m,ls,sum+tsum1,tsum1);
        tsum2+=tsum1;
        tsum=tsum2;
        if (tmp1==-1) ret=tmp2;
        else ret=tmp1;
    }
    return ret;
}

void init(){
    int i;
    for(i=1;i<=n;++i){
        if (o[i]==1) tim[i]=b[i];
        else tim[i]=a[i];
    }
    sort(tim+1,tim+n+1);

    for(i=1;i<=n;++i){
        if (o[i]==1) {pos[i]=getpos(b[i]);r[pos[i]]=i;}
        else {pos[i]=getpos(a[i]);r[pos[i]]=i;}
    }
    build(1,n,1);
}


char s[50];

int main(){
    //freopen("4967in.txt","r",stdin);
    int cas,i;
    cas=0;
    while(scanf("%d",&n)&&n){
        memset(a,-1,sizeof(a));
        memset(b,-1,sizeof(b));
        for(i=1;i<=n;++i){
            scanf("%s",s);

            if (s[1]=='u') o[i]=1;
            else if (s[1]=='o') o[i]=2;
            else o[i]=3;
            if (o[i]==1) scanf("%d%d",&a[i],&b[i]);
            else scanf("%d",&a[i]);
        }
        init();

        printf("Case #%d:\n",++cas);
        for(i=1;i<=n;++i){
            if (o[i]==1){
                insert(pos[i],1,1);
            }
            else if (o[i]==2){
                insert(pos[i],1,-1);
            }
            else{
                int tsum=0;
                printf("%d\n",a[r[query(1,pos[i],1,0,tsum)]]);
            }
        }
    }
    return 0;
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值