题目链接:http://poj.org/problem?id=3667
题意:一家旅馆,有n个房间,一开始房间全是空的,现在我们进行两个操作,输入1,再输入一个x,则是找到连续x个空房,并且要求房间号尽量的小,找到的话就住进去;输入2,再输入x,y,则是退房,x~y之间的所有房间变成没有人居住。
这题是一个典型的线段树区间合并的问题,有关线段树的区间合并问题的讲解网上都比较少啊,这里小编详细的讲解一下线段树的区间合并问题,我们这里用到三个数组 sum,lsum,rsum,分别代表这个区间里面最长的连续空房数,包含区间左端点的最长的空房数,以及包含右端点的最长连续空房数,不难想到如果lsum[rt>>1] == m-(m>>1)也就是区间长度的时候lsum[rt] = lsum[rt<<1] + lsum[rt<<1|1],否则lsum[rt] = lsum[rt<<1],rsum同理,这就是我们的PushUp向上更新,而因为我们这里的更新是区间更新,我们没有必要每一次更新都更新到叶节点上,我们同样可以用到延迟更新的方法,这就是我们的PushDown向下更新,接下来就是Query查询,我们在查询连续x个空房间的时候如果当前结点的左结点的sum[rt<<1]大于x的话,我们就继续在左结点的区间里继续查询,同理,就在右节点的区间里查询,如果rsum[rt<<1]+lsum[rt<<1] > x的话,那么说明两个区间中间交接的地方可能会有,此时我们就可以直接输出mid - rsum[rt<<1] + 1.
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n, m;
const int maxn = 50005;
int sum[maxn << 2];//用于保存当前结点所代表区间的最长连续空房间
int lsum[maxn << 2];//用于保存包含当前结点最左房间在内的一段最长连续空房间
int rsum[maxn << 2];//用于保存包含当前结点最右房间在内的一段最长连续空房间
int lazy[maxn << 2];//延迟标记
void Build(int l, int r, int rt)//建树
{
sum[rt] = lsum[rt] = rsum[rt] = r - l + 1;//初始的时候都是空房间
lazy[rt] = -1;
if(l == r) return;
int mid = (l + r) >> 1;
Build(l, mid, rt << 1);
Build(mid + 1, r, rt << 1 | 1);
}
void PushDown(int rt, int m)//往下更新
{
if(lazy[rt] != -1)//需要往下更新
{
lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];
//把这段房间置为空或置为住满
lsum[rt << 1] = rsum[rt << 1] = sum[rt << 1] = lazy[rt] ? 0 : m - (m >> 1);
lsum[rt << 1 | 1] = rsum[rt << 1 | 1] = sum[rt << 1 | 1] = lazy[rt] ? 0 : (m >> 1);
lazy[rt] = -1;
}
}
void PushUp(int rt, int m)//往上更新
{
lsum[rt] = lsum[rt << 1];//父节点的左区间先赋值为左孩子的左区间
rsum[rt] = rsum[rt << 1 | 1];//父节点的右区间先赋值为右孩子的右区间
if(lsum[rt] == m - (m >> 1)) lsum[rt] += lsum[rt << 1 | 1];//如果左区间满了则继续往右扩张
if(rsum[rt] == (m >> 1)) rsum[rt] += rsum[rt << 1];//如果右区间满了则继续往左扩张
//父节点的最大连续空房间取决于左区间,右区间和中间那个区间的最大值
sum[rt] = max(rsum[rt << 1] + lsum[rt << 1 | 1], max(sum[rt << 1], sum[rt << 1 | 1]));
}
void Update(int L, int R, int c, int l, int r, int rt)//更新
{
if(L <= l && r <= R)
{
sum[rt] = lsum[rt] = rsum[rt] = c ? 0 : r - l + 1;
lazy[rt] = c;
return;
}
PushDown(rt, r - l + 1);
int mid = (l + r) >> 1;
if(L <= mid) Update(L, R, c, l, mid, rt << 1);
if(mid < R) Update(L, R, c, mid + 1, r, rt << 1 | 1);
PushUp(rt, r - l + 1);
}
int Query(int w, int l, int r, int rt)//查询
{
if(l == r)
return l;
PushDown(rt, r - l + 1);
int mid = (l + r) >> 1;
if(sum[rt << 1] >= w) return Query(w, l, mid, rt << 1);
else if(rsum[rt << 1] + lsum[rt << 1 | 1] >= w)
return mid - rsum[rt << 1] + 1;//分而治之的思想关键,这里用于求出房间数大于1的所有答案
else return Query(w, mid + 1, r, rt << 1 | 1);
}
int main()
{
scanf("%d %d", &n, &m);
Build(1, n, 1);//建树
for(int i = 0; i < m; i++)
{
int op, a, b;
scanf("%d", &op);
if(op == 1)
{
scanf("%d", &a);
if(sum[1] < a) puts("0");
else {
int pos = Query(a, 1, n, 1);
printf("%d\n", pos);
Update(pos, pos + a - 1, 1, 1, n, 1);
}
}
else
{
scanf("%d %d", &a, &b);
Update(a, a + b - 1, 0, 1, n, 1);
}
}
return 0;
}