poj3667线段树

题意:输入n,m;  n代表1-n的区间,m代表有m个操作。对于每个操作如果先输入1,则输入w,找到区间最左边未被占用的连续长度为w的区间,返回左端点,并把这段区间标记为已被占用。如果整个区间没有找到符合条件的区间。则返回0。如果输入为2,则将这段区间标记为未被占用。

解法:维护3个变量sum,lsum,rsum,sum代表的是这段区间最大连续区间的长度。lsum是从这个区间第一个元素开始连续未被占用的区间的长度,rsum是当前区间最右端开始连续未被占用的区间长度。对于每次查询,如果sum<w直接返回0。从第一段区间开始如果他的左儿子的最大连续未被占用区间的长度大于w,则查询左子树。否则如果左儿子的rsum+右儿子的lsum>=w,则返回左子树从右往左最后一个未被用占用的点。否则查询右子树。查询完后更新,更新的时候如果cover标记为1则覆盖,cover标记为0则清空。注意写法。

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 100005
using namespace std;
struct node{
    int l,r,lz;
    int sum,lsum,rsum;
    int cover;  //cover=-1没有标记 cover=0清零操作 cover=1覆盖操作
}a[N<<2];
void pushdown(int k)
{
    int m=a[k].r-a[k].l+1;
    if(a[k].cover!=-1){
        a[k<<1].cover=a[k].cover;
        a[k<<1|1].cover=a[k].cover;
        if(a[k].cover==0){
            a[k<<1].lsum=a[k<<1].rsum=a[k<<1].sum=m-(m>>1);
            a[k<<1|1].lsum=a[k<<1|1].rsum=a[k<<1|1].sum=m>>1;
        }
        else{
            a[k<<1].lsum=a[k<<1].rsum=a[k<<1].sum=0;
            a[k<<1|1].lsum=a[k<<1|1].rsum=a[k<<1|1].sum=0;
        }
        a[k].cover=-1;
    }
}
void pushup(int k)
{
    int m=a[k].r-a[k].l+1;
    a[k].lsum=a[k<<1].lsum;
    a[k].rsum=a[k<<1|1].rsum;
    if(a[k].lsum==m-(m>>1))
        a[k].lsum+=a[k<<1|1].lsum;
    if(a[k].rsum==(m>>1))
        a[k].rsum+=a[k<<1].rsum;
    a[k].sum=max(a[k<<1|1].lsum+a[k<<1].rsum,max(a[k<<1].sum,a[k<<1|1].sum));
}
void build(int l,int r,int k)
{
    a[k].l=l,a[k].r=r,a[k].sum=a[k].lsum=a[k].rsum=(r-l+1);
    a[k].cover=-1;
    if(l==r){
        return ;
    }
    else{
        int mid=(l+r)>>1;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
    }
}

int query(int l,int r,int w,int k)
{
    if(l==r){
        return l;
    }
    else{
        pushdown(k);
        int mid=(l+r)>>1;
        if(a[k<<1].sum>=w){
            return query(l,mid,w,k<<1);
        }
        else if(a[k<<1].rsum+a[k<<1|1].lsum>=w){
            return mid-a[k<<1].rsum+1;
        }
        else{
            return query(mid+1,r,w,k<<1|1);
        }
    }
}
void update(int l,int r,int co,int k)
{
    if(a[k].l==l && a[k].r==r){
        a[k].cover=co;
        if(co){
            a[k].sum=a[k].lsum=a[k].rsum=0;
        }
        else{
            a[k].sum=a[k].lsum=a[k].rsum=a[k].r-a[k].l+1;
        }
        return ;
    }
    else{
        pushdown(k);
        int mid=(a[k].l+a[k].r)>>1;
        if(mid>=r){
            update(l,r,co,k<<1);
        }
        else if(mid<l){
            update(l,r,co,k<<1|1);
        }
        else{
            update(l,mid,co,k<<1);
            update(mid+1,r,co,k<<1|1);
        }
        pushup(k);
    }
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        build(1,n,1);
        while(m--){
            int cao;
            scanf("%d",&cao);
            if(cao==1){
                int w;
                scanf("%d",&w);
                if(a[1].sum<w){
                    printf("0\n");
                }
                else{
                    int ans=query(1,n,w,1);
                    printf("%d\n",ans);
                    update(ans,ans+w-1,1,1);
                    //cout<<1<<endl;
                }
            }
            else{
                int x,y;
                scanf("%d%d",&x,&y);
                update(x,x+y-1,0,1);
            }
       }
    }
    return 0;
}


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值