|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
using namespace std;
|
|
|
const int N = 200010;
|
|
|
|
|
|
// n个房间,m个操作
|
|
|
int n, m;
|
|
|
|
|
|
// 线段树,最大空房数,左起最大空房数,右起最大空房数
|
|
|
struct Node {
|
|
|
int l, r; // 范围[l,r]
|
|
|
int mx; // 区间内最大连续空房数
|
|
|
int lx, rx; // 从左开始或从右开始的最大连续空房数
|
|
|
int tag; // tag:懒标记,1 开房, :2 退房,0: 默认值
|
|
|
} tr[N];
|
|
|
|
|
|
// 根据左右儿子的统计信息,更新父亲的统计信息,也就是更新父亲的统计属性
|
|
|
void pushup(int u) {
|
|
|
// 左后+右前,左半边最大长度,右半边最大长度,三者的最大值
|
|
|
// ① mx:区间内最长连续空房
|
|
|
tr[u].mx = max({tr[u << 1].rx + tr[u << 1 | 1].lx, tr[u << 1].mx, tr[u << 1 | 1].mx});
|
|
|
|
|
|
// ② tr[u].lx:父亲节点的最长左前缀,通过tr[u<<1],tr[u<<1|1]的信息来组装
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1; // 这个mid一般都有用,套路性的写上
|
|
|
|
|
|
// mid 是划给左儿子的,所以 mid - tr[u].l + 1 就是左儿子的控制范围,而tr[u].r - mid 就是右儿子的控制范围
|
|
|
// 结合实际场景,利用左右儿子的信息,更新父亲的lx,rx信息
|
|
|
// 如果左儿子整体区间都是空的,那么 父亲的左半边最大长度=左儿子区间长度+右儿子左半边最大长度
|
|
|
if (tr[u << 1].mx == mid - tr[u].l + 1)
|
|
|
tr[u].lx = tr[u << 1].mx + tr[u << 1 | 1].lx;
|
|
|
else
|
|
|
// 否则,左儿子的左半边最大长度,就是父亲的左半边最大长度
|
|
|
tr[u].lx = tr[u << 1].lx;
|
|
|
|
|
|
// ③ rx:最长右后缀
|
|
|
// 如果右儿子的整体区间都是空的,那么 父亲的右半边最长度=右儿子区间长度+左儿子右半边最大长度
|
|
|
if (tr[u << 1 | 1].mx == tr[u].r - mid)
|
|
|
tr[u].rx = tr[u << 1].rx + tr[u << 1 | 1].mx;
|
|
|
else // 否则,右儿子的右半边最大长度,就是父亲的右半边最大长度
|
|
|
tr[u].rx = tr[u << 1 | 1].rx;
|
|
|
}
|
|
|
|
|
|
// 构建线段树
|
|
|
void build(int u, int l, int r) {
|
|
|
tr[u].l = l, tr[u].r = r; // 标记范围
|
|
|
if (l == r) { // 叶子
|
|
|
// 初始时都是空房间,连续空房长度就是区间长度:1,
|
|
|
// 从本区间内,左边向右数有1个空白房间,
|
|
|
// 从右边向左数,也有1个空白房间
|
|
|
tr[u].mx = tr[u].lx = tr[u].rx = 1;
|
|
|
return;
|
|
|
}
|
|
|
int mid = (l + r) >> 1;
|
|
|
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
|
|
|
pushup(u);
|
|
|
}
|
|
|
|
|
|
// 向下传递更新信息
|
|
|
void pushdown(int u) {
|
|
|
if (tr[u].tag) { // 如果存在懒标记
|
|
|
tr[u << 1].tag = tr[u << 1 | 1].tag = tr[u].tag; // 向左右儿子传递这个懒标记
|
|
|
|
|
|
// 根据实际的变化情况,修改左右儿子的统计信息
|
|
|
if (tr[u].tag == 1) { // 开房,则全部为1,左、右儿子的三个统计信息都需要修改为0
|
|
|
tr[u << 1].mx = tr[u << 1].lx = tr[u << 1].rx = 0;
|
|
|
tr[u << 1 | 1].mx = tr[u << 1 | 1].lx = tr[u << 1 | 1].rx = 0;
|
|
|
} else { // 退房
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
// 退房后,整个区间全是0,左半区间中 左最长=右最长=整个区间长=mid-l+1
|
|
|
// 退房后,整个区间全是0,右半区间中 左最长=右最长=整个区间长=r-mid
|
|
|
tr[u << 1].mx = tr[u << 1].lx = tr[u << 1].rx = mid - tr[u].l + 1;
|
|
|
tr[u << 1 | 1].mx = tr[u << 1 | 1].lx = tr[u << 1 | 1].rx = tr[u].r - mid;
|
|
|
};
|
|
|
// 将本节点的懒标记修改为0,表示已经完成了懒标记的向下传递
|
|
|
tr[u].tag = 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void modify(int u, int l, int r, int v) {
|
|
|
if (tr[u].l >= l && tr[u].r <= r) { // 完全包含了当前区间[l,r],那么所有内部节点都需要进行修改
|
|
|
if (v == 1) // 1:开房
|
|
|
tr[u].mx = tr[u].lx = tr[u].rx = 0; // 统计信息全面修改为0,因为全住上人了,没有空的了
|
|
|
else // 2:退房
|
|
|
tr[u].mx = tr[u].lx = tr[u].rx = tr[u].r - tr[u].l + 1; // 退房嘛,整个区间都是空白的,左边最长=右边最长=整个区间最长
|
|
|
// 打上懒标记(有的懒标记是可以累加的,这里是不可以的)
|
|
|
tr[u].tag = v;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 下传懒标记
|
|
|
pushdown(u);
|
|
|
// 分裂
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
if (l <= mid) modify(u << 1, l, r, v);
|
|
|
if (r > mid) modify(u << 1 | 1, l, r, v);
|
|
|
// 向上更新统计信息
|
|
|
pushup(u);
|
|
|
}
|
|
|
|
|
|
// 以u为根节点的树中,控制范围是[l,r],找出连续空白房间个数>=x的区间,返回此区间的左边界
|
|
|
int query(int u, int x) {
|
|
|
// 分裂前先进行懒标记下传
|
|
|
pushdown(u);
|
|
|
int mid = (tr[u].l + tr[u].r) >> 1;
|
|
|
// 因为要获取最小的房间号,所以,必须是左,中,右这个顺序来进行检查,否则可能返回的不是最小房间号
|
|
|
if (tr[u << 1].mx >= x) return query(u << 1, x);
|
|
|
if (tr[u << 1].rx + tr[u << 1 | 1].lx >= x) return mid - tr[u << 1].rx + 1; // 直接找到开始点
|
|
|
if (tr[u << 1 | 1].mx >= x) return query(u << 1 | 1, x);
|
|
|
// 如果不存在x这么多个连续空白,则返回0
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
// 文件输入输出
|
|
|
#ifndef ONLINE_JUDGE
|
|
|
freopen("P2894.in", "r", stdin);
|
|
|
#endif
|
|
|
// 加快读入
|
|
|
ios::sync_with_stdio(false), cin.tie(0);
|
|
|
cin >> n >> m;
|
|
|
|
|
|
// 构建线段树
|
|
|
build(1, 1, n);
|
|
|
|
|
|
while (m--) {
|
|
|
int op;
|
|
|
cin >> op;
|
|
|
if (op == 1) { // 查询连续x个空房 且 入住
|
|
|
int x;
|
|
|
cin >> x;
|
|
|
if (tr[1].mx >= x) { // 如果目前整体的最大值满足条件,可以找出x个连续空房
|
|
|
int l = query(1, x);
|
|
|
printf("%d\n", l);
|
|
|
|
|
|
// 如果找到的合适的起点,从l开始,连续x个房间住上人
|
|
|
if (l) modify(1, l, l + x - 1, 1);
|
|
|
} else
|
|
|
puts("0"); // 找不到x个连续空房
|
|
|
} else { // 退房
|
|
|
int x, y;
|
|
|
cin >> x >> y;
|
|
|
// 2:退房
|
|
|
modify(1, x, x + y - 1, 2);
|
|
|
}
|
|
|
}
|
|
|
return 0;
|
|
|
} |