POJ3667 Hotel(线段树区间合并+lazy标记)

本文介绍了一种使用线段树数据结构解决旅馆房间分配问题的方法。旅馆有连续的房间,需要处理客户入住和退房请求,确保分配连续的房间给客户。通过维护线段树中的关键信息如最大连续空房间数等,可以高效地处理各种请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

The cows are journeying north to Thunder Bay in Canada to gain
cultural enrichment and enjoy a vacation on the sunny shores of Lake
Superior. Bessie, ever the competent travel agent, has named the
Bullmoose Hotel on famed Cumberland Street as their vacation
residence. This immense hotel has N (1 ≤ N ≤ 50,000) rooms all located
on the same side of an extremely long hallway (all the better to see
the lake, of course).

The cows and other visitors arrive in groups of size Di (1 ≤ Di ≤ N)
and approach the front desk to check in. Each group i requests a set
of Di contiguous rooms from Canmuu, the moose staffing the counter. He
assigns them some set of consecutive room numbers r..r+Di-1 if they
are available or, if no contiguous set of rooms is available, politely
suggests alternate lodging. Canmuu always chooses the value of r to be
the smallest possible.

Visitors also depart the hotel from groups of contiguous rooms.
Checkout i has the parameters Xi and Di which specify the vacating of
rooms Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1). Some (or all) of those rooms
might be empty before the checkout.

Your job is to assist Canmuu by processing M (1 ≤ M < 50,000)
checkin/checkout requests. The hotel is initially unoccupied.

Input

  • Line 1: Two space-separated integers: N and M
  • Lines 2..M+1: Line i+1 contains request expressed as one of two possible formats: (a) Two space separated integers representing a
    check-in request: 1 and Di (b) Three space-separated integers
    representing a check-out: 2, Xi, and Di

Output

  • Lines 1…..: For each check-in request, output a single line with a single integer r, the first room in the contiguous sequence of rooms
    to be occupied. If the request cannot be satisfied, output 0.

Sample Input

10 6
1 3
1 3
1 3
1 3
2 5 5
1 6

Sample Output

1
4
7
0
5

思路

首先说明题意:有一个旅馆,里面有n个房间,刚开始所有的房间都是空的,现在给你一个m,接下来有m行,每行第一个数op是操作,当操作值为1的时候,说明现在有一个查询,第二个数k代表要在旅馆要开连续的k个房间,如果可以开房,那就输出这k个房间的第一个房间的位置,如果旅馆住不下就输出0.当操作数op2的时候,接下来给出两个数l,r代表一个区间以l为起点,从l从左往右数r个位置的人退房,也就是清空这个区间。

首先暴力不可取,利用线段树做,当时学线段树的时候太懒了,没学区间合并,导致今天凉了,所以还是要补一补相关知识…

因为是区间问题,所以肯定要用到lazy标记,还需要额外三个数组:

  • msum[rt]:当前端点代表的区间最大连续空房间的个数
  • lsum[rt]:当前端点代表的区间从左到右最大连续空房间的个数
  • rsum[rt]:当前端点代表的区间从右到左最大连续空房间的个数
  • cover[rt]:lazy标记,-1代表不操作,0代表此区间置空,1代表置满

先说明cover标记一共有3个值,-1代表没有操作,1代表要住人,0代表退房

在更新的时候可以通过c的值来直接对rsum,lsum,msum进行赋值,还要给相关节点打上lazy标记。先向下更新,在回溯的时候向上更新,向下更新的时候和普通的线段树基本一样,在向上更新的时候要注意,先更新当前端点从左到右的左子树和从右到左的右子树,当lsum左子树区间住满的时候,证明还可以住人,就加上它右子树区间的值,当rsum的右子树区间住满的时候,就可以加上左子树区间的值

查询的时候,先找msum左儿子是否足够,然后如果不够就找lsum左儿子的右区间和rsum右儿子的左区间的和是否足够,如果两个都不够的话就找右儿子(这个时候右儿子就肯定满足了)。

具体看代码…

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
const int N=50000+20;
int lsum[N<<2],rsum[N<<2],msum[N<<2],cover[N<<2];
/*
msum[rt]:当前端点代表的区间最大连续空房间的个数
lsum[rt]:当前端点代表的区间从左到最大连续空房间的个数
rsum[rt]:当前端点代表的区间从右到左最大连续空房间的个数
cover[rt]:lazy标记,-1代表不操作,0代表此区间置空,1代表置满
*/
void pushup(int rt,int m)//向上更新
{
    //更新当前端点从左到右的左子树和从右到左的右子树
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    if(lsum[rt]==m-(m>>1))//当左子树区间住满
        lsum[rt]+=lsum[rt<<1|1];
    if(rsum[rt]==(m>>1))//当右子树区间住满
        rsum[rt]+=rsum[rt<<1];
    //这个大区间连续的个数为:这个区间的左右子树的最大连续长度和从左到右的右子树+从右到左的左子树
    msum[rt]=max(lsum[rt<<1|1]+rsum[rt<<1],max(msum[rt<<1],msum[rt<<1|1]));
}
void pushdown(int rt,int m)
{
    if(~cover[rt])//当存在延迟标记
    {
        cover[rt<<1]=cover[rt<<1|1]=cover[rt];
        msum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=cover[rt]?0:m-(m>>1);
        msum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=cover[rt]?0:(m>>1);
        cover[rt]=-1;
    }
}
void build(int l,int r,int rt)
{
    cover[rt]=-1;
    lsum[rt]=rsum[rt]=msum[rt]=r-l+1;
    if(l==r) return;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        msum[rt]=lsum[rt]=rsum[rt]=c?0:r-l+1;//如果要增加人,就变成0,如果是消去人,就变成区间的长度
        cover[rt]=c;//进行lazy标记
        return;
    }
    pushdown(rt,r-l+1);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,c,lson);
    if(R>m) update(L,R,c,rson);
    pushup(rt,r-l+1);
}
int query(int w,int l,int r,int rt)//查询能放下w个位置的最左端点
{
    if(l==r) return l;
    pushdown(rt,r-l+1);
    int m=(l+r)>>1;
    if(msum[rt<<1]>=w)
        return query(w,lson);
    else if(rsum[rt<<1]+lsum[rt<<1|1]>=w)//从右到左连续的左子树 +从左到右连续的右子树
        return m-rsum[rt<<1]+1;
    else
        return query(w,rson);
}
int main()
{
    int n,m,op,k,a,b;
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        while(m--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d",&k);
                if(msum[1]<k)//当整个区间连续房间数小于k时
                    puts("0");
                else
                {
                    int p=query(k,1,n,1);//查询最小的房间位置
                    printf("%d\n",p);
                    update(p,p+k-1,1,1,n,1);//更新区间[p,p+k-1]
                }
            }
            else if(op==2)
            {
                scanf("%d%d",&a,&b);
                update(a,a+b-1,0,1,n,1);//更新区间[a,a+b-1]
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值