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 Di (b) Three space-separated integers representing a check-out: 2, Xi, and Di
* 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.
10 6 1 3 1 3 1 3 1 3 2 5 5 1 6
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);
}
}
}