poj 3667

线段树之区间合并

之前多校整过一道,整了两天才弄懂= =……现在整了半个上午……而且还严重参考了大牛代码=    =

以后还得再做一次,经典题目多做有益身心╮(╯▽╰)╭


贴代码:(和大牛代码基本一样= =,只是变量名字不同)

#include <iostream>
#include <cstdio>
#include <cstring>
#define lson l,mid,dex<<1 /************************注意在下的宏定义......免得楼下出现神奇变量看不懂....*********/
#define rson mid+1,r,dex<<1|1
#define havemid int mid = (l+r)>>1
#define left (dex<<1)
#define right (dex<<1|1)
using namespace std;
const int maxx = 51000;
int tage[maxx<<2];//延迟标志,有3个值,-1表示无需更新,1表示有人入住需要把空房间数清零,0表示有人离开,需要把空房间数堆满
int rooml[maxx<<2];//该区间左端开始的空房间数目
int roomr[maxx<<2];//该区间右端开始的空房间数目
int roommid[maxx<<2];//该区间里最长的连续空房间的数目
void Push_down(int dex,int x)
{
    if(tage[dex]!=-1)
    {
        tage[dex<<1] = tage[dex<<1|1] = tage[dex];//延迟标志向下传递,不解释
        rooml[right] = roomr[right] = roommid[right] = tage[dex]?0:(x>>1);//如果延迟标志为0,则将区间内空房间数清零(详见楼上注解)
        rooml[left] = roomr[left] = roommid[left] = tage[dex]?0:(x-(x>>1));//如果延迟标志为1,则将区间内空房间数堆满
        tage[dex] = -1;//表示本区间已经更新
    }
}
void Push_up(int dex,int x)
{
    rooml[dex] = rooml[left];//无论左端开始数的空房间数是否充满整个空间(即即使要加上右儿子从左端开始数的空房间数),都要先把左儿子的房间数复制给父亲的rooml
    roomr[dex] = roomr[right];//同上
    if(rooml[left] == (x-(x>>1))) rooml[dex]+=rooml[right];//如果左儿子的rooml充满区间,则加上右儿子的rooml
    if(roomr[right] == (x>>1))    roomr[dex]+=roomr[left];//如果右儿子的roomr充满区间,则加上左儿子的roomr
    roommid[dex] = max(roomr[left] + rooml[right],max(roommid[left], roommid[right]));//roomr[left] + rooml[right]表示合并区间时产生的连续不断的空房间数
}
void build(int l,int r,int dex)//不解释= =
{
    tage[dex] = -1;
    rooml[dex] = roomr[dex] = roommid[dex] = r-l+1;
    if(l==r)    return;
    havemid;//宏定义
    build(lson);//宏定义+1
    build(rson);//宏定义+2
}

void updata(int L,int R,int mode, int l,int r,int dex)
{
    if(L<=l && R>=r)//如果目前区间为搜索区间的子区间,则子区间被完全覆盖
    {
        rooml[dex] = roomr[dex] = roommid[dex] = mode?0:r-l+1;//根据延迟标志选择性更新
        tage[dex] = mode;
        return ;
    }
    Push_down(dex, r-l+1);//在楼下需要用到儿子节点的语句之前,先行一步向下更新(还没更新的)儿子节点
    havemid;
    if(L<=mid)  updata(L,R,mode,lson);//如果左儿子还有没更新的就更新
    if(R>mid)   updata(L,R,mode,rson);//略同上
    Push_up(dex,r-l+1);//合并区间
}

int query(int x,int l,int r,int dex)
{
    if(l==r)    return l;//搜到子节点则返回 (经测试,返回0竟然也没问题= =,估计数据不怎么样?)
    Push_down(dex,r-l+1);//更新未更新的儿子节点
    havemid;
    if(roommid[left] >= x)   return query(x,lson);//一直找左儿子的最大连续空房间,一直找到左儿子的roommid不满足,则这个区间一定落在中间的区间,否则在右区间
    else if(rooml[dex<<1|1]+roomr[dex<<1]>=x) return mid - roomr[dex<<1]+1;
    else return query(x,rson);
}

int main()//不解释
{
//    freopen("123.txt","r",stdin);
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        build(1,n,1);
        for(int i=1; i<=m; i++)
        {
            int a;
            scanf("%d",&a);
            if(a==1)
            {
                int b;
                scanf("%d",&b);
                if(roommid[1]<b)    puts("0");
                else
                {
                    int ans = query(b,1,n,1);
                    printf("%d\n",ans);
                    updata(ans,ans+b-1,1,1,n,1);
                }
            }
            else
            {
                int u,v;
                scanf("%d %d",&u,&v);
                updata(u,u+v-1,0,1,n,1);
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值