区间合并 区间更新 求第一个条件的子段首下标(住宿问题)

本文提供了一种使用线段树解决POJ3667 Hotel问题的方法,通过维护区间内连续空房间的最大长度来高效地处理查询和更新操作。

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

题目链接   poj3667 Hotel  (此题代码有详细注释,作为初学者可以参考代码理解)

题目大意:让你对一个区间进行操作。输入Q C 或者 Q C D。

    Q  ==1  输入 C: 表示让你求1-n中是否有连续的C个空hotel,如果有多个连续的C个空hotel,则取最左边的,并输出最左边的第一间hotel标号。让人住进去,那么这些空房就不能住人了。如果不存在连续的C个hotel,则输出 0。

    Q==2   输入 C D: 表示 从标号C到C+D-1的hotel的房间全部要退房,那么这么hotel都变成空。

解题思路:记录整段区间最长的空房间。

#include <cstdio>
 #include <cmath>
 #include <iostream>
 #include <algorithm>
 using namespace std;
 
 #define lz 2*u,l,mid
 #define rz 2*u+1,mid+1,r
 const int maxn=50005;
 int  flag[4*maxn];   ///标记
 
 struct node
 {
     int lm; ///从左边第一个点开始最长的连续空hotel
     int rm; ///以右边最后一个结束的最长的连续空hotel
     int sm; ///整段区间最大的连续空hotel 
 } tree[4*maxn];
 
 void push_up(int u, int l, int r)   ///向上更新
 {
     tree[u].lm=tree[2*u].lm;          
     tree[u].rm=tree[2*u+1].rm;
     tree[u].sm=max(tree[2*u].sm,tree[2*u+1].sm);
     int mid=(l+r)>>1;
     if(tree[2*u].lm==mid-l+1) tree[u].lm+=tree[2*u+1].lm;  ///!!这里注意,当左孩子左边连续的达到整个区间时,要加上右孩子的左边区间
     if(tree[2*u+1].rm==r-mid) tree[u].rm+=tree[2*u].rm;   ///!!考虑右区间,同上
     int t=tree[2*u].rm+tree[2*u+1].lm;
     if(t>tree[u].sm) tree[u].sm=t;
 }
 
 void push_down(int u, int l, int r)  ///向下更新
 {
     if(flag[u]==-1) return ;
     if(flag[u])
     {
         flag[2*u]=flag[2*u+1]=flag[u];
         tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=0;
         tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=0;
         flag[u]=-1;
     }
     else
     {
         flag[2*u]=flag[2*u+1]=flag[u];
         int mid=(l+r)>>1;
         tree[2*u].lm=tree[2*u].rm=tree[2*u].sm=mid-l+1;
         tree[2*u+1].lm=tree[2*u+1].rm=tree[2*u+1].sm=r-mid;
         flag[u]=-1;
     }
 }
 
 void build(int u, int l, int r)  ///建树
 {
     flag[u]=-1;
     if(l==r)
     {
         tree[u].lm=tree[u].rm=tree[u].sm=1;
         return ;
     }
     int mid=(l+r)>>1;
     build(lz);
     build(rz);
     push_up(u,l,r);
 }
 
 void Update(int u, int l, int r, int tl, int tr, int c)   ///更新操作
 {
     if(tl<=l&&r<=tr)
     {
         tree[u].sm=tree[u].lm=tree[u].rm=(c==1?0:r-l+1);
         flag[u]= c;  
         return ;
     }
     push_down(u,l,r);   ///再次遇见此段区间时,延迟标记同步向下更新
     int mid=(l+r)>>1;
     if(tr<=mid) Update(lz,tl,tr,c);
     else if(tl>mid) Update(rz,tl,tr,c);
     else
     {
         Update(lz,tl,mid,c);    ///注意区间分隔开,tl,tr跨越两个左右区间
         Update(rz,mid+1,tr,c);
     }
     push_up(u,l,r);     ///递归的时候同步向上更新
 }
 
 int Query(int u, int l, int r, int num)   ///询问操作
 {
     if(l==r)
         return l;
     push_down(u,l,r);     ///延迟标记向下传递
     int mid=(l+r)>>1;
     if(tree[2*u].sm>=num) return Query(lz,num);
     else if(tree[2*u].rm+tree[2*u+1].lm>=num&&tree[2*u].rm>=1) return mid-tree[2*u].rm+1;   ///满足条件时,返回左边rm连续的hotel第一个房间标号
     else
         return Query(rz,num);
 }
 
 int main()
 {
     int n, m;
     while(~scanf("%d%d",&n,&m))
     {
         build(1,1,n);
         while(m--)
         {
             int p, u, v;
             scanf("%d",&p);
             if(p==1)
             {
                 scanf("%d",&u);
                 if(tree[1].sm<u)  ///特判一下是否有这么多个连续的空hotel,没有则直接输出,不用操作
                 {
                     puts("0"); continue;
                 }
                 int p=Query(1,1,n,u);
                 printf("%d\n",p);
                 Update(1,1,n,p,p+u-1,1);
             }
             else
             {
                 scanf("%d%d",&u,&v);
                 Update(1,1,n,u,u+v-1,0);
             }
         }
     }
     return 0;
 }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值