【线段树】USACO 08FEB Hotel G
题目解析
查询操作看起来有点麻烦,但完全可以用二分的思路。
只需要记录下左右儿子有多少个连续的空房间就可以判断起点在左边还是右边了。
当然,还要考虑区间合并,所以又是喜闻乐见的类似于维护最大子段和的方法。
维护这样一些域值:
该区间内连续的最大空房间数
包含左端点的连续的最大空房间数
包含右端点的连续的最大空房间数
懒标记,表示这个区间全满/全空
由于要维护懒标记,和维护爸爸的,所以需要知道是否全空,也就是要维护一个表示区间长度
►Code View
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define N 50005
#define DEL 100000
#define INF 0x3f3f3f3f
int rd()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
return f*x;
}
int n,m;
struct node{
int sum,lsum,rsum,len;
int tag;//懒标记 tag==1 开房 tag==2退房
}tree[N<<2];
void Build(int i,int l,int r)
{
tree[i].sum=tree[i].lsum=tree[i].rsum=tree[i].len=r-l+1;
tree[i].tag=0;
if(l==r) return ;
int mid=(l+r)>>1;
Build(i<<1,l,mid);
Build(i<<1|1,mid+1,r);
}
void PushUp(int i)
{
tree[i].sum=max(max(tree[i<<1].sum,tree[i<<1|1].sum),tree[i<<1].rsum+tree[i<<1|1].lsum);
if(tree[i<<1].sum==tree[i<<1].len)
tree[i].lsum=tree[i<<1].sum+tree[i<<1|1].lsum;
else tree[i].lsum=tree[i<<1].lsum;
if(tree[i<<1|1].sum==tree[i<<1|1].len)
tree[i].rsum=tree[i<<1|1].sum+tree[i<<1].rsum;
else tree[i].rsum=tree[i<<1|1].rsum;
}
void PushDown(int i)
{
if(tree[i].tag==0) return ;
tree[i<<1].tag=tree[i<<1|1].tag=tree[i].tag;
if(tree[i].tag==1)//开房
{
tree[i<<1].sum=tree[i<<1].lsum=tree[i<<1].rsum=0;
tree[i<<1|1].sum=tree[i<<1|1].lsum=tree[i<<1|1].rsum=0;
}
else
{
tree[i<<1].sum=tree[i<<1].lsum=tree[i<<1].rsum=tree[i<<1].len;
tree[i<<1|1].sum=tree[i<<1|1].lsum=tree[i<<1|1].rsum=tree[i<<1|1].len;
}
tree[i].tag=0;
}
int Query(int i,int l,int r,int len)
{//这个查询有点像分治的样子(不过话说线段树本身就是分治的思想吧
if(l==r) return l;//找到了
PushDown(i);
//不过正常情况下很少会在这里返回? 毕竟叶节点最多只有一个空房间 一般都是在下面返回吧
int mid=(l+r)>>1;
if(tree[i<<1].sum>=len) return Query(i<<1,l,mid,len);//左边房间够多
if(tree[i<<1].rsum+tree[i<<1|1].lsum>=len)//在中间能够找到足够的房间
return mid-tree[i<<1].rsum+1;
return Query(i<<1|1,mid+1,r,len);
}
void Update(int i,int l,int r,int ql,int qr,int opt)
{//opt==1 入住 opt==2 退房
if(ql<=l&&r<=qr)
{
if(opt==1) tree[i].sum=tree[i].lsum=tree[i].rsum=0;
else tree[i].sum=tree[i].lsum=tree[i].rsum=tree[i].len;
tree[i].tag=opt;
return ;
}
PushDown(i);
int mid=(l+r)>>1;
if(ql<=mid) Update(i<<1,l,mid,ql,qr,opt);
if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr,opt);
PushUp(i);
}
int main()
{
n=rd(),m=rd();
Build(1,1,n);
while(m--)
{
int opt=rd();
if(opt==1)
{
int x=rd();
if(tree[1].sum<x)
{//没有连续x个空房间
puts("0");
continue;
}
int l=Query(1,1,n,x);
printf("%d\n",l);
Update(1,1,n,l,l+x-1,1);
}
else
{
int l=rd(),x=rd();
Update(1,1,n,l,l+x-1,2);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现