题目链接: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; }