线段树 (区间合并)【p2894】[USACO08FEB]酒店Hotel
Descripion
奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。
贝茜一行,以及其他慕名而来的旅游者,都是一批批地来到旅馆的服务台,希望能订到D_i (1 <= D_i <= N)间连续的房间。服务台的接待工作也很简单:如果存在r满足编号为r..r+D_i-1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。
旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字X_i、D_i描述,表示编号为X_i..X_i+D_i-1 (1 <= X_i <= N-D_i+1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。
而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M (1 <= M < 50,000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。
表示这题很坑爹.
但是的确是一道线段树好题.
线段树需要维护的东西是这些
struct cod{
int l;//左起最长连续空房长度
int r;//右起最长连续空房长度
int len;//区间长度.
int tg;//lazy标记
int sum;//区间最长连续空房长度
}tr[N<<4];
知道维护的东西了,现在我们就考虑如何更新,如何下放标记.
PS:这里以\(1\)表示有人住房.\(2\)表示退房
下放操作是这样的.//应该不难理解.
inline void down(int o)
{
if(!tr[o].tg)return;
tr[ls].tg=tr[rs].tg=tr[o].tg;//传递lazy标记
tr[ls].l=tr[ls].r=tr[ls].sum= (tr[o].tg==1 ? 0:tr[ls].len);//判断情况
tr[rs].l=tr[rs].r=tr[rs].sum= (tr[o].tg==1 ? 0:tr[rs].len);//判断情况
tr[o].tg=0;//清除lazy标记
}
然后考虑向上更新.
我们需要维护\(tr[o].l\)和\(tr[o].r\),还有\(tr[o].sum\)
维护\(tr[o].l\),需要考虑左子区间是否全部是空房.()
如果全部是,那么我们的
\(tr[o].l=tr[ls].sum+tr[rs].l\)
否则就是\(tr[o].l=tr[ls].l\)
这就类似于区间合并.//不是很难理解吧.
维护\(tr[o].r\)类似于维护\(tr[o].l\).
维护\(tr[o].sum\)就有三种情况,
- 要么是\(tr[ls].sum\)
- 要么是\(tr[rs].sum\)
- 要么是\(tr[ls].r+tr[rs].l\)
取\(max\)即可
inline void up(int o)
{
if(tr[ls].sum==tr[ls].len)
tr[o].l=tr[ls].sum+tr[rs].l;
else
tr[o].l=tr[ls].l;
if(tr[rs].sum==tr[rs].len)
tr[o].r=tr[rs].sum+tr[ls].r;
else
tr[o].r=tr[rs].r;
tr[o].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].r+tr[rs].l);
}
其他操作不是很难理解,就不过多解释了.
代码
#include<cstdio>
#include<cctype>
#include<iostream>
#define ls o<<1
#define rs o<<1|1
#define N 50008
#define R register
using namespace std;
inline void in(int &x)
{
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-'0';s=getchar();}
x*=f;
}
struct cod{
int l;
int r;
int len;
int tg;
int sum;
}tr[N<<4];
void build(int o,int l,int r)
{
tr[o].l=tr[o].r=tr[o].len=tr[o].sum=(r-l+1);
if(l==r)return;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
inline void down(int o)
{
if(!tr[o].tg)return;
tr[ls].tg=tr[rs].tg=tr[o].tg;
tr[ls].l=tr[ls].r=tr[ls].sum= (tr[o].tg==1 ? 0:tr[ls].len);
tr[rs].l=tr[rs].r=tr[rs].sum= (tr[o].tg==1 ? 0:tr[rs].len);
tr[o].tg=0;
}
inline void up(int o)
{
if(tr[ls].sum==tr[ls].len)
tr[o].l=tr[ls].sum+tr[rs].l;
else
tr[o].l=tr[ls].l;
if(tr[rs].sum==tr[rs].len)
tr[o].r=tr[rs].sum+tr[ls].r;
else
tr[o].r=tr[rs].r;
tr[o].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].r+tr[rs].l);
}
void change(int o,int l,int r,int x,int y,int del)
{
down(o);
if(x<=l and y>=r)
{
tr[o].tg=del;
if(del==1) tr[o].l=tr[o].r=tr[o].sum=0;
else tr[o].l=tr[o].r=tr[o].sum=tr[o].len;
return;
}
int mid=(l+r)>>1;
if(x<=mid)change(ls,l,mid,x,y,del);
if(y>mid)change(rs,mid+1,r,x,y,del);
up(o);
}
int query(int o,int l,int r,int x)
{
down(o);
if(l==r)return l;
int mid=(l+r)>>1;
if(tr[ls].sum>=x) return query(ls,l,mid,x);
if(tr[ls].r+tr[rs].l>=x) return mid-tr[ls].r+1;
return query(rs,mid+1,r,x);
}
int n,m;
int main()
{
in(n),in(m);build(1,1,n);
for(R int i=1,opt,x,y;i<=m;i++)
{
in(opt);
if(opt==1)
{
in(x);
if(tr[1].sum<x)
{
puts("0");
continue;
}
int pos=query(1,1,n,x);
printf("%d\n",pos);
change(1,1,n,pos,pos+x-1,1);
}
else
{
in(x),in(y);
change(1,1,n,x,x+y-1,2);
}
}
}
线段树 (区间合并)【p2894】[USACO08FEB]酒店Hotel的更多相关文章
- 区间连续长度的线段树——洛谷P2894 [USACO08FEB]酒店Hotel
https://www.luogu.org/problem/P2894 #include<cstdio> #include<iostream> using namespace ...
- 浅谈线段树 (例题:[USACO08FEB]酒店Hotel)By cellur925
今天我们说说线段树. 我个人还是非常欣赏这种数据结构的.(逃)因为它足够优美,有递归结构,有左子树和右子树,还有二分的思想. emm这个文章打算自用,就不写那些基本的操作了... 1° 简单的懒标记( ...
- 【线段树区间合并】BZOJ1593-[Usaco2008 Feb]Hotel 旅馆
好无聊,以前写过没什么好讲的,水过.戳 #include<iostream> #include<cstdio> #include<cstdlib> #define ...
- 线段树||BZOJ1593: [Usaco2008 Feb]Hotel 旅馆||Luogu P2894 [USACO08FEB]酒店Hotel
题面:P2894 [USACO08FEB]酒店Hotel 题解:和基础的线段树操作差别不是很大,就是在传统的线段树基础上多维护一段区间最长的合法前驱(h_),最长合法后驱(t_),一段中最长的合法区间 ...
- POJ 3667 Hotel(线段树 区间合并)
Hotel 转载自:http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html [题目链接]Hotel [题目类型]线段树 ...
- 线段树(区间合并) POJ 3667 Hotel
题目传送门 /* 题意:输入 1 a:询问是不是有连续长度为a的空房间,有的话住进最左边 输入 2 a b:将[a,a+b-1]的房间清空 线段树(区间合并):lsum[]统计从左端点起最长连续空房间 ...
- poj3667 Hotel (线段树 区间合并)
poj3667 HotelTime Limit: 3000MS Memory Limit: 65536KTotal Submissions: 18925 Accepted: 8242Descripti ...
- Poj 3667——hotel——————【线段树区间合并】
Hotel Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 13124 Accepted: 5664 Descriptio ...
- 【bzoj1593】[Usaco2008 Feb]Hotel 旅馆 线段树区间合并
题目描述 奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光.作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿.这个巨大的旅馆一共有N (1 <= N & ...
- P2894 [USACO08FEB]酒店Hotel
P2894 [USACO08FEB]酒店Hotel 简单的线段树维护区间信息. 维护三个值,一个是从左端点能拓展的长度,一个是从右端点能脱产的的长度.另一个是整个区间内的最大连续零一长度. 记录这三个 ...
随机推荐
- [LINUX]警告:检测到时钟错误。您的创建可能是不完整的。
[LINUX]警告:检测到时钟错误.您的创建可能是不完整的. 原因: 如果上一次编译时为20071001,你把系统时间改成20070901后再编译就会报这样的错误. 解决: 把时间 ...
- Winpcap网络开发库入门
原文链接地址:http://www.cnblogs.com/phinecos/archive/2008/10/20/1315176.html Winpcap是一个强大的网络开发库,可以实现许多功能:获 ...
- 【BZOJ 1647】[Usaco2007 Open]Fliptile 翻格子游戏 模拟、搜索
第一步我们发现对于每一个格子,我们只有翻和不翻两种状态,我们发现一旦确定了第一行操作,那么第二行的操作也就随之确定了,因为第一行操作之后我们要想得到答案就得把第一行全部为0,那么第二行的每一个格子的操 ...
- codevs 1078 最小生成树 kruskal
题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助. 约翰已经给他的农场安排了一条高速的网络线路,他想把这 ...
- 查找算法总结Java实现
之前对查找算法做的一些简单总结与实现: 查找算法时间复杂度: 1.二分查找的实现(待补充) public class Test { //循环实现二分查找 public static int binar ...
- HDU5748---(记录每个元素的 最长上升子序列 nlogn)
分析: 给一个序列,求出每个位置结尾的最长上升子序列 O(n^2) 超时 #include "cstdio" #include "algorithm" #def ...
- js实现2048小游戏
这是学完javascript基础,编写的入门级web小游戏 游戏规则:在玩法规则也非常的简单,一开始方格内会出现2或者4等这两个小数字,玩家只需要上下左右其中一个方向来移动出现的数字,所有的数字就会想 ...
- bzoj2811 [Apio2012]Guard
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2811 [题解] 首先我们先把没看到忍者的段去掉,可以用线段树做. 如果剩下的就是K,那么特判 ...
- HDU1068 (二分图最大匹配匈牙利算法)
Girls and Boys Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- Git服务器安装详解及安装遇到问题解决方案【转】
转自:http://www.cnblogs.com/grimm/p/5368777.html git是一个不错的版本管理的工具.现在自己在搞一个简单的应用程序开发,想使用git来进行管理.在Googl ...