题目链接:http://poj.org/problem?id=3667
题目要求的是查询一段区间,更新一段区间,这是线段树最擅长的事了!
该题有两个主要操作,订房和退房,其实可以把订房分解为两个操作,查询(query)和更新(update),而退房就是更新(update)。
本题最难的地方在于节点状态的设计,它需要有三个额外的域:lval(记录该节点从左边开始的连续未订房间的数目), rval(记录该节点从右边开始的连续未订房间的数目), sval(记录该节点最大连续未订房间的数目),有了这三个域,每次更新子节点的状态后,返回父节点时,需要根据子节点的状态来同步更新父节点的状态。在更新一段区间的时候,不必每次都更新到最下层,可以使用lazy思想,这里的lazy思想也不需要做标记,可以根据当前节点sval的值来判断,如果当前节点的sval的值为0,那么它的子节点的sval一定为0, 如果当前节点的sval为它的区间长度,那么它的子节点的sval也一定为区间长度,详见代码。
#include <cstdio>
#define MAX 50005
#define max(a, b) (a > b ? a : b)
struct Node
{
int left, right;
int lval;//记录该节点从左边开始的连续未订房间的数目
int rval;//记录该节点从右边开始的连续未订房间的数目
int sval;//记录该节点最大连续未订房间的数目
}tr[4*MAX];
//建树
void build(int left, int right, int root)
{
tr[root].left = left, tr[root].right = right;
tr[root].lval = tr[root].rval = tr[root].sval = right - left + 1;
if(left < right)
{
int mid = (left+right) >> 1;
build(left, mid, root<<1);
build(mid+1, right, (root<<1) | 1);
}
}
//查询
int query(int a, int root)
{
if(tr[root].sval < a) return 0;
if(tr[root].lval >= a) return tr[root].left;
if(tr[root<<1].sval >= a)
return query(a, root<<1);
else if(tr[root<<1].rval + tr[(root<<1) | 1].lval >= a)
return tr[root<<1].right - tr[root<<1].rval + 1;
else
return query(a, (root<<1) | 1);
}
//获得节点root的区间长度
int getLength(int root)
{
return tr[root].right - tr[root].left + 1;
}
//根据val的值重置节点
void resetNode(int val, int root)
{
tr[root].lval = tr[root].rval = tr[root].sval = getLength(root)*val;
}
//更新节点状态,更新时采用lazy思想,所以不用每次都更新到最下层
void update(int left, int right, int val, int root)
{
if(tr[root].left == left && tr[root].right == right)
{
resetNode(val, root);
return;
}
//lazy思想
if(tr[root].sval == 0)
{
resetNode(0, root<<1);
resetNode(0, (root<<1) | 1);
}
if(tr[root].sval == getLength(root))
{
resetNode(1, root<<1);
resetNode(1, (root<<1) | 1);
}
int mid = (tr[root].left + tr[root].right) >> 1;
if(right <= mid)
update(left, right, val, root<<1);
else if(left > mid)
update(left, right, val, (root<<1) | 1);
else
{
update(left, mid, val, root<<1);
update(mid+1, right, val, (root<<1) | 1);
}
//区间合并
tr[root].lval = tr[root<<1].lval;
if(tr[root<<1].lval == getLength(root<<1))
tr[root].lval += tr[(root<<1) | 1].lval;
tr[root].rval = tr[(root<<1) | 1].rval;
if(tr[(root<<1) | 1].rval == getLength((root<<1) | 1))
tr[root].rval += tr[root<<1].rval;
tr[root].sval = max(max(tr[root<<1].sval, tr[(root<<1) | 1].sval), tr[root<<1].rval + tr[(root<<1) | 1].lval);
}
int main()
{
int N, M, c, a, b, p;
scanf("%d %d", &N, &M);
build(1, N, 1);
for(int i=0; i<M; i++)
{
scanf("%d", &c);
if(c == 1)
{
scanf("%d", &a);
//在这里把订房操作分为两步
//首先查询是否有符合要求的区间,然后更新
p = query(a, 1);
if(p)
update(p, p+a-1, 0, 1);
printf("%d\n", p);
}
else
{
scanf("%d %d", &a, &b);
update(a, a+b-1, 1, 1);
}
}
return 0;
}