hdu3667Hotel

题目大意:

很多旅游团去住宾馆,先到前台登记,要求是要住连在一起的房间。退得时候也是一起退,现在把房间的数量告诉你(1~n)要求你进行如下操作(m次).

1、每次有旅游团来的时候查询是否有满足条件(di个连续的房间)的空余的房间,如果有,安排入住,并且返回第一间房的房号(1~n).如果没有返回0

2、每次有有旅游团退房的时候清除该旅游团(xi~xi+di-1)所住的房间。

解题思路:

        虽然知道是线段树的题目,但是平时敲得太少的无法下手,所以看了下大神的代码,模仿着写了一遍。

基本思路是,每个线段树的节点都标记区间左边界往右的连续房间数和右边界往左的连续房间数,以及该区间的最大的连续房间数,查找的时候能往左找就往左找,因为房间的起始值应该尽可能的小的。然后更新的时候没必要把所有节点都更新了,这个我前面已经简单的介绍过lazy思想的,不理解的自己百度一下。

贴个代码,注释写的很全面了,自己看吧。

#include<stdio.h>
#define N 50005
#define lson root<<1,l,mid
#define rson root<<1|1,mid+1,r
int mmax(int a,int b)
{
    return a>b?a:b;
}
struct node
{
  int lsum,rsum,sum;
  int lazy;
};
node tree[N<<1];
//创建线段树,lsum节点往右的连续,rsum表示节点右端往左的连续,sum表示二者中的最大值,lazy用于lazy标记
void cr(int root,int l,int r)
{
    tree[root].lazy=-1;
    tree[root].lsum=tree[root].rsum=tree[root].sum=r-l+1;
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    cr(lson);
    cr(rson);
}
void up(int root,int sum)
{
    tree[root].lsum=tree[root<<1].lsum;    //左连续==左子树的左连续
    tree[root].rsum=tree[root<<1|1].rsum; //右连续==右子树的右连续
    if(tree[root<<1].lsum==sum-(sum>>1))      //左子树的连续满了,则左连续可以拓展到右子树的右连续
        tree[root].lsum+=tree[root>>1|1].lsum;
    if(tree[root<<1|1].rsum==(sum>>1))     //右子树连续满了,
        tree[root].rsum+=tree[root>>1].rsum;
    tree[root].sum=mmax(tree[root<<1|1].lsum+tree[root<<1].rsum,mmax(tree[root<<1].sum,tree[1<<1|1].sum));//左右,或左或右
}
void down(int root,int sum)
{
    if(tree[root].lazy!=-1)//-1表示已更新,自然不在更新
    {
        tree[root<<1].lazy=tree[root<<1|1].lazy=tree[root].lazy;//根节点没跟新子节点就没更新,根节点要被清空,则子节点也是被清空。
        tree[root<<1].sum=tree[root<<1].lsum=tree[root<<1].rsum=tree[root<<1].lazy?0:sum-(sum>>1);
        tree[root<<1|1].sum=tree[root<<1|1].lsum=tree[root<<1|1].rsum=tree[root<<1|1].lazy?0:sum>>1;
        tree[root].lazy=-1;//标记根节点已经被更新
    }
}
void update(int st,int ed,int lazy,int root,int l,int r )
{
    if(st<=l&&r<=ed)//把范围内的更新了
    {
            tree[root].lsum=tree[root].rsum=tree[root].sum=lazy?0:r-l+1;
            tree[root].lazy=lazy;
            return ;
    }
    down(root,r-l+1);//顺带把下面的更新了
    int mid=(l+r)>>1;
    if(st<=mid)
    {
        update(st,ed,lazy,lson);
    }
    if(mid<ed)
    {
        update(st,ed,lazy,rson);
    }
    up(root,r-l+1);//把根节点更新了
}
int query(int w,int root,int l,int r)
{
    if(l==r)
        return l;
    down(root,r-l+1);
    int mid=(l+r)>>1;
    if(tree[root<<1].sum>=w)
            return query(w,lson);
    else
        if((tree[root<<1].rsum+tree[root<<1|1].lsum)>=w)
                             return mid-tree[root<<1].rsum+1;
    return query(w,rson);
}
int main()
{

    int n,m,op,di,xi;
    scanf("%d%d",&n,&m);
    cr(1,1,n);
    while(m--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d",&di);
                if(tree[1].sum<di)
                    puts("0");
                else{
                    int st=query(di,1,1,n);
                    printf("%d\n",st);
                    update(st,st+di-1,1,1,1,n);
                    }
                 }
            else{
                scanf("%d%d",&xi,&di);
                update(xi,xi+di-1,0,1,1,n);
            }
    }

}

这个代码耗时900多MS,所以得优化,本篇博客有待更新。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值