POJ 3667 Hotel

本文介绍了一种使用线段树数据结构解决旅馆房间分配问题的方法。旅馆有N个房间,需要处理M次入住和退房请求。通过构建线段树并实现更新和查询操作,确保每次都能找到合适的连续空房间分配给顾客。

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

Time Limit: 3000MS Memory Limit: 65536K

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条指令,指令分两个类型,以1开头的是查询操作,以2开头的是更新操作;
1 X 表示在总区间内查询一个长度为X的空房间,并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0;
2 Xi Di , 表示从单位Xi开始,清除一段长度为Di的区间(就是退房),不需要输出。

【解题思路】 此题用线段树做,该题主要是两个操作,更新操作和查询操作;更新操作相对简单一点,用一个标记记录房间是否入住,在通过向下传递标记和返回值来更新每个节点的lsum(左最大连续区间),rsum(右最大连续区间),nsum(最大连续区间);查询要输出区间的具体位置,则有:
1.如果左子区间的nsum不小于X,查询左子区间;
2.若左子区间的rsum加上右子区间的lsum不小于X,直接返回tr[id*2].r-tr[id*2].rsum+1,注意,不需要再递归;
3.若右子区间的nsum不小于X,查询右子区间;
4.若都不符合,则返回0;

【AC代码】

#include<stdio.h>
#include<algorithm>
#define fff 50005
using namespace std;
struct node
{
    int l,r,vis;
    int lsum,rsum,nsum;
}tr[fff*4];
void pushdown(int id)
{
    if(tr[id].vis!=-1)
    {
        tr[id*2].vis=tr[id*2+1].vis=tr[id].vis;
        tr[id*2].lsum=tr[id*2].rsum=tr[id*2].nsum=(tr[id].vis?0:tr[id*2].r-tr[id*2].l+1); 
        tr[id*2+1].lsum=tr[id*2+1].rsum=tr[id*2+1].nsum=(tr[id].vis?0:tr[id*2+1].r-tr[id*2+1].l+1);
        tr[id].vis=-1;
    }
}
void pushup(int id)
{
    tr[id].lsum=tr[id*2].lsum;
    tr[id].rsum=tr[id*2+1].rsum;
    tr[id].nsum=max(tr[id*2].rsum+tr[id*2+1].lsum,max(tr[id*2].nsum,tr[id*2+1].nsum));
    if(tr[id*2].lsum==tr[id*2].r-tr[id*2].l+1)
        tr[id].lsum=tr[id*2].lsum+tr[id*2+1].lsum;
    if(tr[id*2+1].rsum==tr[id*2+1].r-tr[id*2+1].l+1)
        tr[id].rsum=tr[id*2+1].rsum+tr[id*2].rsum;
}
void build(int id,int l,int r)
{
    tr[id].l=l;tr[id].r=r;
    tr[id].lsum=tr[id].rsum=tr[id].nsum=r-l+1;
    tr[id].vis=-1;  //初始化为-1,入住和退房都可以对线段树进行更新
    if(l==r)
        return;
    int mid=(l+r)/2;
    build(id*2,l,mid);
    build(id*2+1,mid+1,r);
}
void update(int id,int l,int r,int x)
{
    if(tr[id].l==l&&tr[id].r==r)
    {
        tr[id].vis=x;
        tr[id].lsum=tr[id].rsum=tr[id].nsum=(x?0:tr[id].r-tr[id].l+1);
        return;
    }
    pushdown(id);
    int mid=(tr[id].l+tr[id].r)/2;
    if(r<=mid)
        update(id*2,l,r,x);
    else if(l>mid)
        update(id*2+1,l,r,x);
    else
    {
        update(id*2,l,mid,x);
        update(id*2+1,mid+1,r,x);
    }
    pushup(id);
}
int found(int id,int len)
{
    pushdown(id);
    //因为要输出最左区间的位置,下面的判断顺序不可以随便调换,必须是从左到右
    if(tr[id*2].nsum>=len)
        return found(id*2,len);
    else if(tr[id*2].rsum+tr[id*2+1].lsum>=len)
        return tr[id*2].r-tr[id*2].rsum+1;  //排除了左子区间的情况,可以直接返回该区间的位置
    else if(tr[id*2+1].nsum>=len)
        return found(id*2+1,len);
    else
        return 0;
}
int main()
{
    int n,m,x,y,z;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        build(1,1,n);
        while(m--)
        {
            scanf("%d",&x);
            if(x==1)
            {
                scanf("%d",&y);
                int k=found(1,y);
                printf("%d\n",k);
                if(k) //注意,当输出是0的时候,没有人入住,不需要更新
                    update(1,k,k+y-1,1);
            }
            else
            {
                scanf("%d%d",&y,&z);
                update(1,y,y+z-1,0);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值