题意:有N个房间,M次操作。有两种操作(1)“1 a”,表示找到连续的长度为a的空房间,如果有多解,优先左边的,即表示入住,输出左端点。(2)“2 b len”,把起点为b长度的len的房间清空,即退房。
题解:线段树+区间合并+成段更新
第一道区间合并的题。
s
u
m
sum
sum:区间中最长连续子区间。
l
s
u
m
lsum
lsum:区间中从左端点开始的连续区间。
r
s
u
m
rsum
rsum:区间中从右端点开始的连续区间。
区间合并主要是PushUp()中的操作:
//取左子树、右子树、左右子树共同构成的连续区间 三者中的最长
sum[rt] = max(max(sum[rt << 1], sum[rt << 1 | 1]), rsum[rt << 1] + lsum[rt << 1 | 1]);
lsum[rt] = lsum[rt << 1];
rsum[rt] = rsum[rt << 1 | 1];
//从左端点开始的连续区间占满了左子树,继续增加右子树中的从左端点开始的连续区间
if (lsum[rt] == (m - tree[rt].l + 1)) lsum[rt] += lsum[rt << 1 | 1];
//右端点同理
if (rsum[rt] == (tree[rt].r - m)) rsum[rt] += rsum[rt << 1];
回到题目,操作2就是线段树区间更新,主要是操作1。
操作1要求找到连续的长度为a的空房间,我们用1表示房间为空。
因为优先考虑左边的,所以我们这样写:
if (sum[rt] >= c) {
//左子树满足条件
if (sum[rt << 1] >= c) return query(c, rt << 1);
//左右共同区间满足条件,直接可以返回起点
else if (rsum[rt << 1] + lsum[rt << 1 | 1] >= c) return m - rsum[rt << 1] + 1;
//右子树满足条件
else return query(c, rt << 1 | 1);
}
还有就是不要忘了查询到满足条件的进行区间更新。
剩下的就是普通的线段树了。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
//区间合并
const int MAXN = 5e4 + 5;
int sum[MAXN << 2], lsum[MAXN << 2], rsum[MAXN << 2], add[MAXN << 2];
struct Node {
int l, r;
int mid() { return (l + r) >> 1; }
} tree[MAXN << 2];
void PushDown(int rt, int m) {
if (~add[rt]) {
add[rt << 1] = add[rt];
add[rt << 1 | 1] = add[rt];
sum[rt << 1] = lsum[rt << 1] = rsum[rt << 1] = add[rt] * (m - (m >> 1));
sum[rt << 1 | 1] = lsum[rt << 1 | 1] = rsum[rt << 1 | 1] = add[rt] * (m >> 1);
add[rt] = -1;
}
}
void PushUp(int rt) {
int m = tree[rt].mid();
sum[rt] = max(max(sum[rt << 1], sum[rt << 1 | 1]), rsum[rt << 1] + lsum[rt << 1 | 1]);
lsum[rt] = lsum[rt << 1];
rsum[rt] = rsum[rt << 1 | 1];
if (lsum[rt] == (m - tree[rt].l + 1)) lsum[rt] += lsum[rt << 1 | 1];
if (rsum[rt] == (tree[rt].r - m)) rsum[rt] += rsum[rt << 1];
}
void build(int l, int r, int rt) {
tree[rt].l = l;
tree[rt].r = r;
add[rt] = -1;
if (l == r) {
sum[rt] = lsum[rt] = rsum[rt] = 1; //空房间置1
return;
}
int m = tree[rt].mid();
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
PushUp(rt);
}
void update(int c, int l, int r, int rt) {
if (tree[rt].l == l && r == tree[rt].r) {
add[rt] = c;
sum[rt] = lsum[rt] = rsum[rt] = c * (r - l + 1);
return;
}
if (tree[rt].l == tree[rt].r) return;
PushDown(rt, tree[rt].r - tree[rt].l + 1);
int m = tree[rt].mid();
if (r <= m) update(c, l, r, rt << 1);
else if (l > m) update(c, l, r, rt << 1 | 1);
else {
update(c, l, m, rt << 1);
update(c, m + 1, r, rt << 1 | 1);
}
PushUp(rt);
}
int query(int c, int rt) {
if (tree[rt].l == tree[rt].r) {
return tree[rt].l;
}
PushDown(rt, tree[rt].r - tree[rt].l + 1);
int m = tree[rt].mid();
if (sum[rt] >= c) {
if (sum[rt << 1] >= c) return query(c, rt << 1);
else if (rsum[rt << 1] + lsum[rt << 1 | 1] >= c) return m - rsum[rt << 1] + 1;
else return query(c, rt << 1 | 1);
}
return 0;
}
int n, m, id, x, y;
int main() {
scanf("%d%d", &n, &m);
build(1, n, 1);
while (m--) {
scanf("%d", &id);
if (id == 1) {
scanf("%d", &x);
int temp = query(x, 1);
printf("%d\n", temp);
if(temp) update(0, temp, temp + x - 1, 1);
}
else {
scanf("%d%d", &x, &y);
update(1, x, x + y - 1, 1);
}
}
return 0;
}