HOTEL 旅馆

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;
}
上一篇:Weex团队负责人:我眼中的Weex和Weex开源那些事


下一篇:Rtthread object管理