[POJ3667]Hotel

Posted xjr_01

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[POJ3667]Hotel相关的知识,希望对你有一定的参考价值。

[POJ3667]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.

输入

* 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

输出

* 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

数据规模及约定

见“试题描述”

题解

对于每一个位置我们维护它所在的极长空区间。在查询的时候我们搞一个线段树,维护一下每个区间里面的极长空区间长度的最大值,然后我们看如果当前最大值小于询问长度就输出 0,线段树左边的最大值大于等于询问的长度就往左走,否则往右走,知道走到叶节点我们就找到这个位置了;然后我们需要把找到的这一段区间覆盖成非空状态,然后更新一下这段区间后面部分的极长区间信息,如下图:

红色部分就是即将被改成非空状态的区间,绿色部分就是需要更新极长区间信息的部分(极长区间由红绿区间的并变成了绿色区间)

对于第二种操作,我们查询一下左端点 - 1 的极长区间的左端点 ql,右端点 + 1 的极长区间的右端点 qr(注意如果左端点不在极长区间内 ql = 左端点,右端点同理),然后 [ql, qr] 就是一个新的极长区间了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == \'-\') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - \'0\'; c = getchar(); }
	return x * f;
}

#define maxn 50010

int n, maxl[maxn<<2];
struct Int {
	int l, len;
	Int(): l(-1), len(-1) {}
	Int(int _, int __): l(_), len(__) {}
} line[maxn], setv[maxn<<2];
void build(int o, int l, int r) {
	if(l == r) line[l] = Int(1, n), maxl[o] = n;
	else {
		int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
		build(lc, l, mid); build(rc, mid + 1, r);
		maxl[o] = max(maxl[lc], maxl[rc]);
	}
	return ;
}
void pushdown(int o, int l, int r) {
	if(setv[o].l < 0) return ;
	if(l == r){ setv[o] = Int(-1, -1); return ; }
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	setv[lc] = setv[rc] = setv[o]; maxl[lc] = maxl[rc] = setv[o].len;
	if(l == mid) line[l] = setv[o];
	if(mid + 1 == r) line[r] = setv[o];
	setv[o] = Int(-1, -1);
	return ;
}
void update(int o, int l, int r, int ql, int qr, Int v) {
	if(ql > qr) return ;
	pushdown(o, l, r);
	if(ql <= l && r <= qr) {
		setv[o] = v;
		maxl[o] = v.len;
		if(l == r) line[l] = setv[o];
		return ;
	}
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	if(ql <= mid) update(lc, l, mid, ql, qr, v);
	if(qr > mid) update(rc, mid + 1, r, ql, qr, v);
	maxl[o] = max(maxl[lc], maxl[rc]);
	return ;
}
Int query(int o, int l, int r, int x) {
	pushdown(o, l, r);
	if(l == r) return line[l];
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	if(x <= mid) return query(lc, l, mid, x);
	return query(rc, mid + 1, r, x);
}
int Query(int o, int l, int r, int mx) {
	pushdown(o, l, r);
	if(maxl[o] < mx) return 0;
	if(l == r) return line[l].l;
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	if(maxl[lc] >= mx) return Query(lc, l, mid, mx);
	return Query(rc, mid + 1, r, mx);
}

int main() {
	n = read(); int q = read();
	build(1, 1, n);
	while(q--) {
		int tp = read();
		if(tp == 1) {
			int len = read(), ans = Query(1, 1, n, len);
			printf("%d\\n", ans);
			if(ans) {
				Int li = query(1, 1, n, ans);
//				printf("li: [%d, %d]\\n", li.l, li.l + li.len - 1);
//				printf("change: [%d, %d] to zero\\n", ans, ans + len - 1);
				update(1, 1, n, ans, ans + len - 1, Int(0, 0));
//				printf("change: [%d, %d] to [%d, %d]\\n", ans + len, li.l + li.len - 1, ans + len, li.len - len);
				update(1, 1, n, ans + len, li.l + li.len - 1, Int(ans + len, li.len - len));
			}
		}
		else {
			int ql = read(), len = read(), qr = ql + len - 1;
			Int l = ql > 1 ? query(1, 1, n, ql - 1) : Int(1, 1), r = qr < n ? query(1, 1, n, qr + 1) : Int(n, 1);
//			printf("l: [%d, %d]\\nr: [%d, %d]\\n", l.l, l.l + l.len - 1, r.l, r.l + r.len - 1);
			if(l.l) ql = l.l;
			if(r.l) qr = r.l + r.len - 1;
//			printf("[%d, %d]\\n", ql, qr);
			update(1, 1, n, ql, qr, Int(ql, qr - ql + 1));
		}
	}
	
	return 0;
}

另一种方法就是线段树维护区间中最大的空串长度,最大后缀空串长度,最大前缀空串长度,剩下的就很简单了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == \'-\') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - \'0\'; c = getchar(); }
	return x * f;
}

#define maxn 50010

int mxl[maxn<<2], setv[maxn<<2], pre[maxn<<2], suf[maxn<<2];
void build(int o, int l, int r) {
	mxl[o] = pre[o] = suf[o] = r - l + 1;
	setv[o] = -1;
	if(l == r) return ;
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	build(lc, l, mid); build(rc, mid + 1, r);
	return ;
}
void pushdown(int o, int l, int r) {
	int lc = o << 1, rc = lc | 1;
	if(setv[o] < 0) return ;
	if(l == r){ setv[o] = -1; return ; }
	setv[lc] = setv[rc] = setv[o];
	if(setv[o]) mxl[lc] = mxl[rc] = pre[lc] = pre[rc] = suf[lc] = suf[rc] = 0;
	else {
		int mid = l + r >> 1;
		mxl[lc] = pre[lc] = suf[lc] = mid - l + 1;
		mxl[rc] = pre[rc] = suf[rc] = r - mid;
	}
	setv[o] = -1;
	return ;
}
void update(int o, int l, int r, int ql, int qr, int v) {
	pushdown(o, l, r);
	if(ql <= l && r <= qr) {
		setv[o] = v;
		if(v) mxl[o] = pre[o] = suf[o] = 0;
		else mxl[o] = pre[o] = suf[o] = r - l + 1;
		return ;
	}
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	if(ql <= mid) update(lc, l, mid, ql, qr, v);
	if(qr > mid) update(rc, mid + 1, r, ql, qr, v);
	mxl[o] = max(suf[lc] + pre[rc], max(mxl[lc], mxl[rc]));
	pre[o] = (pre[lc] == mid - l + 1 ? pre[lc] + pre[rc] : pre[lc]);
	suf[o] = (suf[rc] == r - mid ? suf[rc] + suf[lc] : suf[rc]);
	return ;
}
int query(int o, int l, int r, int len) {
	pushdown(o, l, r);
	if(mxl[o] < len) return 0;
	if(l == r) return l;
	int mid = l + r >> 1, lc = o << 1, rc = lc | 1;
	if(mxl[lc] >= len) return query(lc, l, mid, len);
	if(suf[lc] + pre[rc] >= len) return mid - suf[lc] + 1;
	return query(rc, mid + 1, r, len);
}

int main() {
	int n = read(), q = read();
	build(1, 1, n);
	while(q--) {
		int tp = read();
		if(tp == 1) {
			int len = read(), ans = query(1, 1, n, len);
			printf("%d\\n", ans);
			if(ans) update(1, 1, n, ans, ans + len - 1, 1);
		}
		else {
			int l = read(), r = l + read() - 1;
			update(1, 1, n, l, r, 0);
		}
	}
	
	return 0;
}

 

以上是关于[POJ3667]Hotel的主要内容,如果未能解决你的问题,请参考以下文章

线段树区间合并POJ3667-Hotel

POJ 3667 Hotel

POJ 3667 Hotel

poj 3667 Hotel - 线段树

[POJ3667]Hotel

Hotel poj 3667