HOTEL 旅馆
这其实就是一个简单版的山海经(也就花了一晚上而已)
虽然暴力也能过,但是还是写个线段树更好一些,锻炼一下自己的能力
题目描述
OIER最近的旅游计划,是到长春净月潭,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。这个巨大的旅馆一共有N间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。 所有的旅游者,都是一批批地来到旅馆的服务台,希望能订到Di间连续的房间。服务台的接待工作也很简单:如果存在r满足编号r...r+Di-1为的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。 旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字Xi,DI描述,表示编号Xi..Xi+Di-1为房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。 而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。
数据范围
1 <= N <= 50000
1 <= Di <= N
1 <= Xi <= N-Di+1
1 <= M < 50000
输入格式
第1行: 2个用空格隔开的整数:N、M
第2..M+1行: 第i+1描述了第i个请求,如果它是一个订房请求,则用2个数字 1,Di 描述,数字间用空格隔开;如果它是一个退房请求,用3 个以空格隔开的数字2,Xi,Di描述
输出格式
第1..?行: 对于每个订房请求,输出1个独占1行的数字:如果请求能被满足 ,输出满足条件的最小的r;如果请求无法被满足,输出0
思路
这道题读完就有思路,暴力的思路肯定是有的(虽然会TLE,但是如果不会写正解,还是要保住一部分分数的),暴力搜索时从每个点向后搜所求区间长度,直到搜到为止。更改时直接将要更改区间全部改完就可以了(0分起步,上不封顶,数据有点水)
暴力AC代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
inline int in(){
int x = 0;
char c = getchar();
while(c > '9' || c < '0'){
c = getchar();
}
while(c <= '9' && c >= '0'){
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
inline void print(int x){
if(x < 0) putchar('-'),x = -x;
if(x > 9) print(x / 10);putchar(x % 10 + '0');
}
int n,m,a,b,c;
int l[50010];
int main(){
freopen("hotel.in","r",stdin);
freopen("hotel.out","w",stdout);
n = in();
m = in();
while(m--){
a = in();
b = in();
if(a == 1){
int f = 1;
int len = 0;
for(int i = 1;i <= n;i++){
if(l[i]) len = 0;
else len++;
if(len == b){
f = i-len+1;
printf("%d\n",f);
break;
}
}
if(len < b) printf("0\n");
else for(int i = f;i <= f+len-1;i++) l[i] = 1;
}else{
c = in();
for(int i = b;i <= b+c-1;i++) l[i] = 0;
}
}
return 0;
}
再仔细想想会发现这道题的写法和山海经差不多,不过并不需要维护那么多东西
正解
首先,需要构建一棵线段树,需要维护三个值,从左端点向右的最大空白,从右端点向左的最大空白区间中最大的空白(包括左右和中间部分)
懒惰标记一定要写,暴力全改可能会得到一个美好的TLE(别问,问就是试过,鄙人只能得到92分)
struct node{
int l,r;//区间左右端点 int lm,rm,mx;//lm从左端点向右的最大空白,rm同理,mx左右包括中间的最大 int lz;//懒惰标记(1有人,-1没人) }t[300000];
然后建树就是正常的建树
建树
inline void build(int rt,int l,int r){//建树
t[rt].l = l;
t[rt].r = r;
t[rt].lz = 0;
t[rt].lm = t[rt].rm = t[rt].mx = t[rt].r - t[rt].l + 1;
if(l == r) return;
int mid = (l + r) >> 1;
build(lson,l,mid);
build(rson,mid+1,r);
}
更新父节点
inline void pushup(int rt){//更新父节点
//如果 左儿子的左侧最大值 等于 区间长度 ,那么 rt的左侧最大值 应该为 左儿子的区间长度 加 右儿子的左侧最大值
if(t[lson].lm == t[lson].r - t[lson].l + 1) t[rt].lm = t[lson].lm + t[rson].lm;
else t[rt].lm = t[lson].lm;
//同理,如果右 儿子的右侧最大值 等于 区间长度 ,那么 rt的右侧最大值 应该为右 儿子的区间长度 加 左儿子的右侧最大值
if(t[rson].rm == t[rson].r - t[rson].l + 1) t[rt].rm = t[rson].rm + t[lson].rm;
else t[rt].rm = t[rson].rm;
//rt的最大值为 左儿子的最大值 和 右儿子的最大值 ,还有 中间部分的最大值 中的最大值
t[rt].mx = max(max(t[lson].mx,t[rson].mx),t[lson].rm + t[rson].lm);
//t[rt].mx是由儿子来更新的,不能取儿子和自己的最大值
}
下放标记
inline void pushdown(int rt){
if(t[rt].lz == 1){//有人
t[rt].lz = 0;//取消自己的标记
t[lson].lz = t[rson].lz = 1;//下放标记
//最大值为0
t[lson].lm = t[lson].mx = t[lson].rm = 0;
t[rson].lm = t[rson].mx = t[rson].rm = 0;
}
else if(t[rt].lz == -1){//没人
t[rt].lz = 0;//取消自己的标记
t[lson].lz = t[rson].lz = -1;//下放标记
//自己的最大值是区间长度
t[lson].lm = t[lson].mx = t[lson].rm = t[lson].r - t[lson].l + 1;
t[rson].lm = t[rson].mx = t[rson].rm = t[rson].r - t[rson].l + 1;
}
}
正解AC代码
正解AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
inline int in(){
int x = 0,f = 0;
char c = getchar();
while(c > '9' || c < '0'){
if(c == '-') f = 1;
c = getchar();
}
while(c <= '9' && c >= '0'){
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
if(f) return -x;
else return x;
}
inline void print(int x){
if(x < 0) putchar('-'),x = -x;
if(x > 9) print(x / 10);putchar(x % 10 + '0');
}
#define lson (rt << 1)
#define rson (rt << 1|1)
struct node {
int l,r;//区间左右端点
int lm,rm,mx;//lm从左端点向右的最大空白,rm同理,mx左右包括中间的最大
int lz;//懒惰标记(1有人,-1没人)
}t[300000];
int n,m,len,st;
inline void pushup(int rt){//更新父节点
//如果 左儿子的左侧最大值 等于 区间长度 ,那么 rt的左侧最大值 应该为 左儿子的区间长度 加 右儿子的左侧最大值
if(t[lson].lm == t[lson].r - t[lson].l + 1) t[rt].lm = t[lson].lm + t[rson].lm;
else t[rt].lm = t[lson].lm;
//同理,如果右 儿子的右侧最大值 等于 区间长度 ,那么 rt的右侧最大值 应该为右 儿子的区间长度 加 左儿子的右侧最大值
if(t[rson].rm == t[rson].r - t[rson].l + 1) t[rt].rm = t[rson].rm + t[lson].rm;
else t[rt].rm = t[rson].rm;
//rt的最大值为 左儿子的最大值 和 右儿子的最大值 ,还有 中间部分的最大值 中的最大值
t[rt].mx = max(max(t[lson].mx,t[rson].mx),t[lson].rm + t[rson].lm);
//t[rt].mx是由儿子来更新的,不能取儿子和自己的最大值
}
inline void pushdown(int rt){
if(t[rt].lz == 1){//有人
t[rt].lz = 0;//取消自己的标记
t[lson].lz = t[rson].lz = 1;//下放标记
//最大值为0
t[lson].lm = t[lson].mx = t[lson].rm = 0;
t[rson].lm = t[rson].mx = t[rson].rm = 0;
}
else if(t[rt].lz == -1){//没人
t[rt].lz = 0;//取消自己的标记
t[lson].lz = t[rson].lz = -1;//下放标记
//自己的最大值是区间长度
t[lson].lm = t[lson].mx = t[lson].rm = t[lson].r - t[lson].l + 1;
t[rson].lm = t[rson].mx = t[rson].rm = t[rson].r - t[rson].l + 1;
}
}
inline void build(int rt,int l,int r){//建树
t[rt].l = l;
t[rt].r = r;
t[rt].lz = 0;
t[rt].lm = t[rt].rm = t[rt].mx = t[rt].r - t[rt].l + 1;
if(l == r) return;
int mid = (l + r) >> 1;
build(lson,l,mid);
build(rson,mid+1,r);
}
inline void update(int rt,int l,int r,int bj){//区间更新
//先下放标记,防止自己的值不对
pushdown(rt);
if(l <= t[rt].l && t[rt].r <= r){//如果自己被更改区间包含,更改自己的值和标记
if(bj){
t[rt].lz = 1;
t[rt].lm = t[rt].mx = t[rt].rm = 0;
}
else{
t[rt].lz = -1;
t[rt].lm = t[rt].mx = t[rt].rm = t[rt].r - t[rt].l + 1;
}
return;
}
int mid = (t[rt].l + t[rt].r) >> 1;
if(l <= mid) update(lson,l,r,bj);
if(mid < r) update(rson,l,r,bj);
pushup(rt);//别忘了更新父节点
}
inline int ask(int rt){//区间查询
pushdown(rt);//下放标记
//如果搜到叶子节点,直接返回叶子节点的左端点
if(t[rt].l == t[rt].r) return t[rt].l;
//按照左中右的顺序来搜
//左
if(t[lson].mx >= len) return ask(lson);
//中
if(t[lson].rm + t[rson].lm >= len) return t[lson].r - t[lson].rm + 1;
//右
if(t[rson].mx >= len) return ask(rson);
}
int main(){
freopen("hotel.in","r",stdin);
freopen("hotel.out","w",stdout);
int a;
n = in();
m = in();
build(1,1,n);
while(m--){
a = in();
if(a&1){//如果a为1,要住人
len = in();
int pos = 0;
if(t[1].mx < len) pos = 0;//如果最大的区间长度也不够
//说明没有满足条件的范围
else{ //否则一定能找到一个合适的范围
pos = ask(1);//找到左端点
update(1,pos,pos+len-1,1);//更新这个范围内的值
}
print(pos);
printf("\n");
}else{
st = in();
len = in();
update(1,st,st+len-1,0);//输入的是长度,右端点应该为st+len-1
}
}
return 0;
}