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 r to 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
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,m ,n代表有n个房间,编号为1—n,开始都为空房,m表示以下有m行操作,以下 每行先输入一个数 i ,表示一种操作:
若i为1,表示查询房间,再输入一个数x,表示在1–n 房间中找到长度为x的连续空房,输出连续x个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为x的连续空房,输出0。若找得到,在这x个空房间中住上人。
若i为2,表示退房,再输入两个数 x,y 代表 房间号 x—x+y-1 退房,即让房间为空。
解题思路
首先可以确定这是一个区间修改单点查询的问题,那么就要用到线段树的延迟操作
这里他有两个区间的查询操作,一个是退房一个是入房,那么我们的lazy就标记为两种,根据题意咱们设定lazy=1时是入房,lazy=2时是退房
我们这里维护的是三个参数,一个是区间左侧可空出的房间最大值,一个是区间右侧可空出房间最大值,一个是这个区间可以空出的房间的最大值。
代码一共需要分为creat,pushup,pushdown,update,query这5步操作。
那么接下来我们一起来探究一下,这五个操作如何去维护这三个操作。
首先我们应当构造一个结构体,里面包含三个参数并且包含lazy标记。
const int N = 100000;
struct node {
ll sum, rmaxn, lmaxn;
int lazy;
}tree[N * 4 + 7];
1. creat
先上代码
void creat(int l, int r, int rt)
{
int len = r - l + 1;
int mid = l + r >> 1;
tree[rt] = node{ len,len,len,0 };
if (l == r)return;
creat(l, mid, rt << 1);
creat(mid + 1, r, rt << 1 | 1);
}
creat的作用很显然就是初始化这棵树,那么这棵树包含的信息有什么呢?
- 左最值
- 右最值
- 整体最大值
- lazy标记
因为最初房间都是空的,所以可用的范围都是区间长度,既r-l+1
最初都没有lazy标记
接着深搜构造这棵线段树
2.pushup
void push_up(int l, int r, int rt)
{
int mid = l + r >> 1;
if (tree[rt << 1].sum == mid - l + 1)
tree[rt].lmaxn = tree[rt << 1].sum + tree[rt << 1 | 1].lmaxn;
else
tree[rt].lmaxn = tree[rt << 1].lmaxn;
if (tree[rt << 1 | 1].sum == r - mid)
tree[rt].rmaxn = tree[rt << 1 | 1].sum + tree[rt << 1].rmaxn;
else
tree[rt].rmaxn = tree[rt << 1 | 1].rmaxn;
tree[rt].sum = tree[rt << 1].rmaxn + tree[rt << 1 | 1].lmaxn;
tree[rt].sum = max(tree[rt << 1 | 1].sum, max(tree[rt << 1].sum, tree[rt].sum));
}
我们想一下,pushup的操作是操作进入回溯阶段,父亲节点根据左右子节点去重新维护自己的值的过程。
自己的左最值可以是自己的左孩子加右孩子的左侧,前提是左孩子的整个区间都是空房间
否则只能是左孩子的左最值
同理
自己的左最值可以是自己的右孩子加左孩子的右侧,前提是右孩子的整个区间都是空房间
否则只能是右孩子的右最值
下面该维护整个区间的最大值了,我们可以知道,整个区间的最大值可以由左孩子和右孩子的最大值来决定,这是数据原来就有的数据,但是合并之后就会出现一个新的数据,那就是左孩子的右最值和右孩子的左最值。
3. pushdown
void push_down(int l, int r, int rt)
{
int mid = l + r >> 1;
if (tree[rt].lazy)
{
if (tree[rt].lazy == 1)
{
tree[rt << 1].lazy = tree[rt << 1 | 1].lazy = 1;
tree[rt << 1].lmaxn = tree[rt << 1].rmaxn = tree[rt << 1].sum = 0;
tree[rt << 1 | 1].lmaxn = tree[rt << 1 | 1].rmaxn = tree[rt << 1 | 1].sum = 0;
}
else
{
tree[rt << 1].lazy = tree[rt << 1 | 1].lazy = 2;
tree[rt << 1].lmaxn = tree[rt << 1].rmaxn = tree[rt << 1].sum = mid - l + 1;
tree[rt << 1 | 1].lmaxn = tree[rt << 1 | 1].rmaxn = tree[rt << 1 | 1].sum = r - mid;
}
tree[rt].lazy = 0;
}
}
pushdown操作主要是传递lazy标记的操作,同时把状态下压。
如果当前的标记为入房,那么下传后的标记的三个参数的值应该都为0,因为没有房间可以入住
如果当前的标记为退房,那么下传后的标记的三个参数的值都应该是这个区间的长度,因为所有房间都可以入住。
最后把当前节点的lazy标记计为0.
4. update
void update(int L, int R, int mark, int l, int r, int rt)
{
if (L <= l && r <= R)
{
if (mark == 1)
tree[rt].sum = tree[rt].lmaxn = tree[rt].rmaxn = 0;
else
tree[rt].sum = tree[rt].lmaxn = tree[rt].rmaxn = r - l + 1;
tree[rt].lazy = mark;
return;
}
if (tree[rt].lazy)push_down(l, r, rt);
int mid = l + r >> 1;
if (L <= mid)update(L, R, mark, l, mid, rt << 1);
if (R > mid)update(L, R, mark, mid + 1, r, rt << 1 | 1);
push_up(l, r, rt);
}
如果当前的区间完全属于目标区间,那么这个区间整体加lazy标记,否则的话,就判断目标区间和当前区间的关系,去递归这个区间关系。经过非包含的点的时候要下压lazy标记
最后一直向下修改之后,返回时要pushup操作。
5. query
ll query(int len, int l, int r, int rt)
{
if (l == r)return l;
if (tree[rt].lazy)push_down(l, r, rt);
int mid = l + r >> 1;
if (tree[rt << 1].sum >= len)return query(len, l, mid, rt << 1);
if (tree[rt << 1].rmaxn + tree[rt << 1 | 1].lmaxn >= len)return mid - tree[rt << 1].rmaxn + 1;
return query(len, mid + 1, r, rt << 1 | 1);
}
查询过程中,如果当前区间的l和r相等那么就表示查询到头了,一般情况下,这是对于长度为1的情况的处理方法,因为只有查询区间长度为1才会走到叶子节点。
否则的话,我们如果存在lazy标记,那么我们下压标记。
那么我们继续来看下面三种情况。我们优先去查询靠左的区间,
- 如果判断出左孩子的最大值依然能够放入整个区间,那么继续遍历左区间。
- 如果左边已经不能满足,那么就看左孩子的右最值和右孩子的左最值能否满足,如果能满足则可以很快的计算出当前合并的区间的左值,就是当前中间的下标减去左孩子右区间长度,因为是长度原因,还需要加上1,这样才能算出下标。
- 首先通过第一个节点的最值就能判断出能不能找到一个合适的区间,满足条件,如果说第一个节点满足那么接下来一定也有满足的,那么左边和中间都不能满足那么就只能在右边了。
我们依次查询这三种状态就可以得到最左侧满足的下标了。
AC代码
//#include<unordered_map>
#include<algorithm>
#include<iostream>
#include<string.h>
#include <iomanip>
#include<stdio.h>
#include<vector>
#include<string>
#include<math.h>
#include<cmath>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll ll_inf = 9223372036854775807;
const int int_inf = 2147483647;
const short short_inf = 32767;
const ll less_inf = 0x3f3f3f3f;
const char char_inf = 127;
#pragma GCC optimize(2)
#define accelerate cin.tie(NULL);cout.tie(NULL);ios::sync_with_stdio(false);
#define PI 3.141592653589793
#define EPS 1.0e-8
ll gcd(ll a, ll b) {
return b ? gcd(b, a % b) : a;
}
ll lcm(ll a, ll b) {
return a * b / gcd(a, b);
}
inline ll read() {
ll c = getchar(), Nig = 1, x = 0;
while (!isdigit(c) && c != '-')c = getchar();
if (c == '-')Nig = -1, c = getchar();
while (isdigit(c))x = ((x << 1) + (x << 3)) + (c ^ '0'), c = getchar();
return Nig * x;
}
inline void out(ll a) {
if (a < 0)putchar('-'), a = -a;
if (a > 9)out(a / 10);
putchar(a % 10 + '0');
}
ll qpow(ll x, ll n, ll mod) {
ll res = 1;
while (n > 0) {
if (n & 1)res = (res * x) % mod;
x = (x * x) % mod;
n >>= 1;
}
return res;
}
#define read read()
const int N = 100000;
struct node {
ll sum, rmaxn, lmaxn;
int lazy;
}tree[N * 4 + 7];
void creat(int l, int r, int rt)
{
int len = r - l + 1;
int mid = l + r >> 1;
tree[rt] = node{ len,len,len,0 };
if (l == r)return;
creat(l, mid, rt << 1);
creat(mid + 1, r, rt << 1 | 1);
}
void push_up(int l, int r, int rt)
{
int mid = l + r >> 1;
if (tree[rt << 1].sum == mid - l + 1)
tree[rt].lmaxn = tree[rt << 1].sum + tree[rt << 1 | 1].lmaxn;
else
tree[rt].lmaxn = tree[rt << 1].lmaxn;
if (tree[rt << 1 | 1].sum == r - mid)
tree[rt].rmaxn = tree[rt << 1 | 1].sum + tree[rt << 1].rmaxn;
else
tree[rt].rmaxn = tree[rt << 1 | 1].rmaxn;
tree[rt].sum = tree[rt << 1].rmaxn + tree[rt << 1 | 1].lmaxn;
tree[rt].sum = max(tree[rt << 1 | 1].sum, max(tree[rt << 1].sum, tree[rt].sum));
}
void push_down(int l, int r, int rt)
{
int mid = l + r >> 1;
if (tree[rt].lazy)
{
if (tree[rt].lazy == 1)
{
tree[rt << 1].lazy = tree[rt << 1 | 1].lazy = 1;
tree[rt << 1].lmaxn = tree[rt << 1].rmaxn = tree[rt << 1].sum = 0;
tree[rt << 1 | 1].lmaxn = tree[rt << 1 | 1].rmaxn = tree[rt << 1 | 1].sum = 0;
}
else
{
tree[rt << 1].lazy = tree[rt << 1 | 1].lazy = 2;
tree[rt << 1].lmaxn = tree[rt << 1].rmaxn = tree[rt << 1].sum = mid - l + 1;
tree[rt << 1 | 1].lmaxn = tree[rt << 1 | 1].rmaxn = tree[rt << 1 | 1].sum = r - mid;
}
tree[rt].lazy = 0;
}
}
void update(int L, int R, int mark, int l, int r, int rt)
{
if (L <= l && r <= R)
{
if (mark == 1)
tree[rt].sum = tree[rt].lmaxn = tree[rt].rmaxn = 0;
else
tree[rt].sum = tree[rt].lmaxn = tree[rt].rmaxn = r - l + 1;
tree[rt].lazy = mark;
return;
}
if (tree[rt].lazy)push_down(l, r, rt);
int mid = l + r >> 1;
if (L <= mid)update(L, R, mark, l, mid, rt << 1);
if (R > mid)update(L, R, mark, mid + 1, r, rt << 1 | 1);
push_up(l, r, rt);
}
ll query(int len, int l, int r, int rt)
{
if (l == r)return l;
if (tree[rt].lazy)push_down(l, r, rt);
int mid = l + r >> 1;
if (tree[rt << 1].sum >= len)return query(len, l, mid, rt << 1);
if (tree[rt << 1].rmaxn + tree[rt << 1 | 1].lmaxn >= len)return mid - tree[rt << 1].rmaxn + 1;
return query(len, mid + 1, r, rt << 1 | 1);
}
int main()
{
int n = read, m = read;
creat(1, n, 1);
while (m--)
{
int mark = read;
if (mark == 1)
{
int len = read;
if (tree[1].sum >= len)
{
ll l = query(len, 1, n, 1);
out(l);
puts("");
update(l, l + len - 1, 1, 1, n, 1);
}
else
puts("0");
}
else
{
int sta = read, len = read;
update(sta, sta + len - 1, 2, 1, n, 1);
}
}
}