POJ - 3667 Hotel

                                              POJ - 3667  Hotel

The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as their vacation residence. This immense hotel has N (1 ≤ N ≤ 50,000) rooms all located on the same side of an extremely long hallway (all the better to see the lake, of course).

The cows and other visitors arrive in groups of size Di (1 ≤ Di ≤ N) and approach the front desk to check in. Each group i requests a set of Di contiguous rooms from Canmuu, the moose staffing the counter. He assigns them some set of consecutive room numbers r..r+Di-1 if they are available or, if no contiguous set of rooms is available, politely suggests alternate lodging. Canmuu always chooses the value of rto be the smallest possible.

Visitors also depart the hotel from groups of contiguous rooms. Checkout i has the parameters Xi and Di which specify the vacating of rooms Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1). Some (or all) of those rooms might be empty before the checkout.

Your job is to assist Canmuu by processing M (1 ≤ M < 50,000) checkin/checkout requests. The hotel is initially unoccupied.


Input

* Line 1: Two space-separated integers: N and M
* Lines 2..M+1: Line i+1 contains request expressed as one of two possible formats: (a) Two space separated integers representing a check-in request: 1 and D(b) Three space-separated integers representing a check-out: 2, Xi, and Di

Output

* Lines 1.....: For each check-in request, output a single line with a single integer r, the first room in the contiguous sequence of rooms to be occupied. If the request cannot be satisfied, output 0.

Sample Input
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
Sample Output
1
4
7
0
5


这道题的题意是有N个(1<=N<=50000)个房间在一排,然后有两种操作,输入1表示租用房间,然后后面的Di表示租的个数,每次房间被租用都是将连续的房间号租出去,并且尽可能租靠前的房间,然后将第一个房间的房间号输出出来。输入2表示房客退房,后面的Xi,Di表示从Xi号房间开始,有Di个房客退房。

这是一道线段树入门题,要用到区间更新,如果不用会超时,区间更新与单点更新的区别就是多一个laz数组,作用是储存延迟更新的标记,需要用到的时候才更新数据,否则不更新,可以节约很多时间。这道题的关键其实是要想清楚线段树的结构体里要储存什么,因为几个连续的空房间可能是左儿子的右半边和右儿子的左半边,所以这题的结构体里除了左右边界以外我还储存了该节点的左连续空间(从该节点的最左端开始连续的空房间),右连续空间,和当前最长的连续空间,一开始做这道题的时候想了很久,AC代码如下

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
int flag, solve, n, flagg, m, flaggg;
int sum;
int N, M;
int laz[5000005];

struct NODE
{
	int value;//当前最长连续空间
	int left;
	int right;
	int ll;//左连续空间
	int rr;//右连续空间
}node[5000005];

void pushdown(int rt)//向下更新一层
{
	if (laz[rt] != -1)//如果延迟标记显示此处需要更新
	{
		laz[rt * 2] = laz[rt * 2 + 1] = laz[rt];//将子节点标记为需要更新
		if (laz[rt] == 1)//如果是要将此处更新为空房间
		{
			int len = node[rt].right - node[rt].left + 1;//空房间的长度
			node[rt * 2].value = node[rt * 2].ll = node[rt * 2].rr = len - (len / 2);
			node[rt * 2 + 1].value = node[rt * 2 + 1].ll = node[rt * 2 + 1].rr = len / 2;
		}
		else//将此处更新为非空房间
		{
			int len = node[rt].right - node[rt].left + 1;
			node[rt * 2].value = node[rt * 2].ll = node[rt * 2].rr = 0;
			node[rt * 2 + 1].value = node[rt * 2 + 1].ll = node[rt * 2 + 1].rr = 0;
		}
		laz[rt] = -1;//更新结束,将此处标记为不再需要更新
	}
}

void Buildtree(int root, int left, int right)//基本的建树过程
{
	node[root].left = left;
	node[root].right = right;
	if (left == right)
	{
		node[root].value = 1;
		node[root].rr = 1;
		node[root].ll = 1;
		return;
	}
	Buildtree(root * 2, left, (right + left) / 2);
	Buildtree(root * 2 + 1, (right + left) / 2 + 1, right);
	node[root].value = node[root * 2].value + node[root * 2 + 1].value;
	node[root].ll = node[root].value;
	node[root].rr = node[root].value;
}

void pushup(int root)//向上更新一层
{
	if (node[root * 2].rr == 0 || node[root * 2 + 1].ll == 0)//如果自己的左儿子和右儿子中间没有连在一起
	{
		node[root].ll = node[root * 2].ll;
		node[root].rr = node[root * 2 + 1].rr;
		node[root].value = max(node[root * 2].value, node[root * 2 + 1].value);
	}
	else
	{
		if (node[root * 2].ll == node[root * 2].right - node[root * 2].left + 1)//如果左儿子每个点都是空房间
			node[root].ll = node[root * 2].ll + node[root * 2 + 1].ll;//该节点的左连续空间等于左儿子的全部空间加右儿子的左连续空间
		else
			node[root].ll = node[root * 2].ll;//否则它的左连续空间就等于左儿子的左连续空间
		if (node[root * 2 + 1].ll == node[root * 2 + 1].right - node[root * 2 + 1].left + 1)//同理
			node[root].rr = node[root * 2 + 1].rr + node[root * 2].rr;
		else
			node[root].rr = node[root * 2 + 1].rr;
		node[root].value = max(max(node[root * 2].value, node[root * 2 + 1].value), node[root * 2].rr + node[root * 2 + 1].ll);//它的最长连续空房间就应该是这三个值的最大值
	}
}


void update(int rt, int b, int e, int c)//更新,将b到e区间更新为c(c=1表示有空房间,c=0表示没空房间)
{
	if (b <= node[rt].left&&node[rt].right <= e)
	{
		laz[rt] = c;//将该点标记记为需要更新成c
		if (c)
			node[rt].value = node[rt].rr = node[rt].ll = node[rt].right - node[rt].left + 1;
		else
			node[rt].value = node[rt].rr = node[rt].ll = 0;
		return;
	}
	pushdown(rt);//向下更新
	int mid = (node[rt].right + node[rt].left) / 2;
	if (b <= mid)update(rt * 2, b, e, c);
	if (e>mid)update(rt * 2 + 1, b, e, c);
	pushup(rt);//往回更新
}

void queryroom(int root, int i)//查找连续空房间
{
	if (node[root].right == node[root].left)
	{
		solve = node[root].right;
		return;
	}
	pushdown(root);
	if (node[root * 2].value >= i)
	{
		queryroom(root * 2, i);
	}
	else if (node[root * 2].rr + node[root * 2 + 1].ll >= i)
	{
		solve = node[root * 2].right - node[root * 2].rr + 1;
		return;
	}
	else if (node[root * 2 + 1].value >= i)
	{
		queryroom(root * 2 + 1, i);
	}
	else
		return;
}

int main()
{
	int T;
	scanf("%d%d", &N, &M);
	memset(laz, -1, sizeof(laz));//在程序开始将标记记为-1表示都不需要更新
	Buildtree(1, 1, N);
	for (int i = 1; i <= M; i++)
	{
		flag = 0;
		flagg = 0;
		flaggg = 0;
		solve = 0;
		scanf("%d", &T);
		if (T == 1)
		{
			scanf("%d", &n);
			if (node[1].value >= n)
			{
				queryroom(1, n);
				update(1, solve, solve + n - 1, 0);
				printf("%d\n", solve);
			}
			else
				printf("0\n");
		}
		if (T == 2)
		{
			scanf("%d%d", &n, &m);
			update(1, n,n+m-1, 1);
		}
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值