poj 3667 hotel 线段树…

    题目意思很简洁,就是多次查询连续空段n的最靠左区间,并覆盖,或者把某段变成空。 
    用线段树维护三个参数,lmaxn,rmax,max分别表示靠左最长连续,靠右最长连续,最长连续。
    修改时,父节点的三个参数都能从子节点参数求得(具体看代码),查询时,也能由子节点和父节点的三个参数递归的得到解答。这个过程不是太好讲明白,得自己想,具体的看代码。
     
#include <iostream>
#include <stdio.h>
#include <string.h>
#define ls t<<1
#define rs t<<1|1
#define midt (tr[t].l+tr[t].r)>>1
using namespace std;
const int maxn=50000+10;

struct
{
    int l,r;
    int lmax,rmax,max;
    int lazy;
}tr[maxn*4];

void maketree(int t,int l,int r)
{
    tr[t].l=l;
    tr[t].r=r;
    tr[t].lmax=tr[t].rmax=tr[t].max=(r-l+1);
    tr[t].lazy=0;
    if(l==r) return;
    int mid=midt;
    maketree(ls,l,mid);
    maketree(rs,mid+1,r);
}

void pushdown(int t)
{
    if(tr[t].lazy==1)
    {
        tr[ls].lmax=tr[ls].rmax=tr[ls].max=tr[ls].r-tr[ls].l+1;
        tr[ls].lazy=tr[t].lazy;
        tr[rs].lmax=tr[rs].rmax=tr[rs].max=tr[rs].r-tr[rs].l+1;
        tr[rs].lazy=tr[t].lazy;
    }
    else
    {
        tr[ls].lmax=tr[ls].rmax=tr[ls].max=0;
        tr[ls].lazy=tr[t].lazy;
        tr[rs].lmax=tr[rs].rmax=tr[rs].max=0;
        tr[rs].lazy=tr[t].lazy;
    }
    tr[t].lazy=0;
}

void modify(int t,int l,int r,int tmp)
{
//     printf("%d %d %d\n",t,tr[t].l,tr[t].r);
    if(l==tr[t].l&&r==tr[t].r)
    {
        tr[t].lazy=tmp;
        if(tmp==1)
        tr[t].lmax=tr[t].rmax=tr[t].max=(r-l+1);
        else
        tr[t].lmax=tr[t].rmax=tr[t].max=0;
        return;
    }

    if(tr[t].lazy)
    pushdown(t);

    int mid=midt;
    if(r<=mid)
    modify(ls,l,r,tmp);
    else if(mid+1<=l)
    modify(rs,l,r,tmp);
    else
    {
        modify(ls,l,mid,tmp);
        modify(rs,mid+1,r,tmp);
    }

    tr[t].max=max(tr[ls].max,tr[rs].max);
    tr[t].max=max(tr[t].max,tr[ls].rmax+tr[rs].lmax);
    tr[t].lmax=tr[ls].lmax;
    tr[t].rmax=tr[rs].rmax;

    if(tr[ls].lmax==(tr[ls].r-tr[ls].l+1))
    tr[t].lmax+=tr[rs].lmax;
    if(tr[rs].rmax==(tr[rs].r-tr[rs].l+1))
    tr[t].rmax+=tr[ls].rmax;
}

int query(int t,int tmp)
{
//     printf("%d\n",t);
    if(tr[t].max<tmp) return(0);

    if(tr[t].lazy)
    pushdown(t);

    if(tr[t].lmax>=tmp)
    {
        printf("%d\n",tr[t].l);
        modify(1,tr[t].l,tr[t].l+tmp-1,-1);
    }
    else if(tr[ls].max>=tmp)
    {
        query(ls,tmp);
    }
    else if(tr[ls].rmax+tr[rs].lmax>=tmp)
    {
        int l=tr[ls].r-tr[ls].rmax+1;
        printf("%d\n",l);
        modify(1,l,l+tmp-1,-1);
    }
    else
    {
        query(rs,tmp);
    }
    return(1);
}


int main()
{
//     freopen("in.txt","r",stdin);
    int n,k;

    int tmp,l,r,ret;
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        maketree(1,1,n);
        for(int i=1;i<=k;i++)
        {
            scanf("%d",&tmp);
            if(tmp==2)
            {
                scanf("%d %d",&l,&r);
                modify(1,l,l+r-1,1);
            }
            else
            {
                scanf("%d",&r);
                ret=query(1,r);
                if(ret==0)
                printf("0\n");
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值