题目大意:
有N间房,排成一排,每次入住连续编号为[r, r + D - 1]的房间,要求r最小;每次退房时会退掉连续编号为[X, X + D - 1]的房间。输入有两种形式:
1 入住D间房间,要求输出r最小
2 退掉一段房间
题目分析:
经典线段树问题。有插入、删除和查询操作。每次入住时查询并插入,退房时删除。
可以通过维护左端空房数,右端空房数和最大空房数三个空间来实现各个操作。
具体实现过程:
lf[]记录左端连续空房
rf[]记录右端连续空房
mf[]记录最长连续空房
dl[]标记该段房间的状态,也就是懒操作:0为空,1为满
update()实现对一段区间的更新
pushdown()使标记下传
query()查询最左所需连续房间编号
实现代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define maxn 50002<<2
#define m ((l+r)>>1)
#define ll (t<<1) // 左孩子
#define rr (t<<1|1) // 右孩子
#define ls l,m,ll // 左孩子参数
#define rs m+1,r,rr //右孩子参数
#define max(a,b) ((a)>(b)?(a):(b))
int lf[maxn], rf[maxn], mf[maxn], dl[maxn];
inline void pushdown(int l, int r, int t) { // 标记下传
if (dl[t] != -1) {
lf[ll] = rf[ll] = mf[ll] = (dl[ll] = dl[t]) ? 0 : (m - l + 1);
lf[rr] = rf[rr] = mf[rr] = (dl[rr] = dl[t]) ? 0 : (r - m);
dl[t] = -1;
}
}
void update(int L, int R, int c, int l, int r, int t) {
if (L <= l && r <= R) {
lf[t] = rf[t] = mf[t] = (dl[t] = c) ? 0 : (r - l + 1);
return;
}
pushdown(l, r, t);
if (L <= m)
update(L, R, c, ls);
if (R > m)
update(L, R, c, rs);
// pushup
lf[t] = lf[ll];
rf[t] = rf[rr];
if (lf[ll] == m - l + 1)
lf[t] += lf[rr];
if (rf[rr] == r - m)
rf[t] += rf[ll];
mf[t] = max(max(mf[ll], mf[rr]), rf[ll] + lf[rr]);
}
int query(int d, int l, int r, int t) { // 查询过程
if (l == r)
return l;
pushdown(l, r, t); // 不要忘了标记下传
if (mf[ll] >= d)
return query(d, ls);
else if (rf[ll] + lf[rr] >= d)
return m - rf[ll] + 1;
else
return query(d, rs);
}
int main() {
int n, cs, c, d, x, p;
scanf("%d %d", &n, &cs);
// 建树
lf[1] = rf[1] = mf[1] = n;
dl[1] = 0;
while (cs--) {
scanf("%d", &c);
if (c == 1) {
scanf("%d", &d);
if (d > mf[1])
printf("0\n");
else {
p = query(d, 1, n, 1);
printf("%d\n", p);
update(p, p + d - 1, 1, 1, n, 1);
}
} else {
scanf("%d %d", &x, &d);
update(x, x + d - 1, 0, 1, n, 1);
}
}
return 0;
}