POJ 3667 Hotel(线段树:区间覆盖,维护最大连续子区间)
http://poj.org/problem?id=3667
题意:
有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作
1 w 表示在总区间内查询一个长度为w的可用区间,并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0 。例如:n=10 , 1 3 查到的最左的长度为3的可用区间就是[1,3],返回1,并且该区间被占用了
2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出
分析:
首先本题类似于UVA1400 ,所有区间合并类的线段树基本都需要维护3类信息:最大区间,最大前缀,最大后缀.
维护一棵线段树,该线段树每个节点维护信息:在该节点所代表的区间内的覆盖情况cover: 为0时表示没有被覆盖,为1时表示被覆盖了,为-1时表示子节点中既有被覆盖的也有没被覆盖的.
最长连续区间长度sub,最长连续区间前缀长度pre,最长后缀suf.一共上面4种信息需要维护.其中cover是线段树的根本,sub,suf和pre是方便快速查询.就像有setv ,addv 和sum的线段树一样. setv和addv是根本,sum是方便查询. sum的值要基于setv和addv来.
当我们在[L,R]查找一个长度为8的连续空闲区间时,这个区间如果有连续8的空闲区间的话,要不就是在左儿子区间,要不就是在中间(即跨越了左儿子和右儿子),要不就是在右儿子区间.
build(i,l,r)操作: 递归建立左右儿子,初始节点的cover都为0,sub=pre=suf=r-l+1.
PushDown(i,num)操作: 如果cover位不是-1才PushDown.且子节点的sub pre 和suf根据cover位的值而变化.以前的cover值不变.
PushUp(i,num)操作: 更新cover位,更新sub pre suf位.
query(x,i,l,r)操作 : 首先如果sub<x直接返回-1,否则先PushDown后先在左子树查找,再在中间查找,再在右子树查找.过程中如果找到直接返回即可.
update(ql,qr,v,i,l,r)操作:如果ql,qr区间包含了l,r区间,那么直接操作,否则PushDown操作后分别处理左右子树即可.最后还要PushUp.
AC代码:625ms
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int MAXN = 50000 + 100; #define lson i*2,l,m #define rson i*2+1,m+1,r #define root 1,1,n int cover[MAXN * 4]; int sub[MAXN * 4], pre[MAXN * 4], suf[MAXN * 4]; void PushDown(int i, int num) { if(cover[i] != -1) { cover[i * 2] = cover[i * 2 + 1] = cover[i]; sub[i * 2] = pre[i * 2] = suf[i * 2] = (cover[i] ? 0 : (num - num / 2)); sub[i * 2 + 1] = pre[i * 2 + 1] = suf[i * 2 + 1] = (cover[i] ? 0 : (num / 2)); } } void PushUp(int i, int num) { sub[i] = max(suf[i * 2] + pre[i * 2 + 1], max(sub[i * 2], sub[i * 2 + 1])); pre[i] = pre[i * 2]; suf[i] = suf[i * 2 + 1]; if(pre[i] == (num - num / 2)) pre[i] += pre[i * 2 + 1]; if(suf[i] == num / 2) suf[i] += suf[i * 2]; if(cover[i * 2] == -1 || cover[i * 2 + 1] == -1) cover[i] = -1; else if(cover[i * 2] != cover[i * 2 + 1]) cover[i] = -1; else cover[i] = cover[i * 2]; } void build(int i, int l, int r) { cover[i] = 0; sub[i] = pre[i] = suf[i] = r - l + 1; if(l == r)return ; int m = (l + r) / 2; build(lson); build(rson); } int query(int w, int i, int l, int r) { if(sub[i] < w)return 0; //此句可以不要,在外面用sub[1]判断即可 if(l == r) return l; PushDown(i, r - l + 1); int m = (l + r) / 2; if(w <= sub[i * 2]) return query(w, lson); else if(w <= suf[i * 2] + pre[i * 2 + 1]) return m - suf[i * 2] + 1; else return query(w, rson); } void update(int ql, int qr, int v, int i, int l, int r) { if(ql <= l && r <= qr) { cover[i] = v; sub[i] = suf[i] = pre[i] = (v ? 0 : r - l + 1); return ; } PushDown(i, r - l + 1); int m = (r + l) / 2; if(ql <= m) update(ql, qr, v, lson); if(m < qr) update(ql, qr, v, rson); PushUp(i, r - l + 1); } int main() { int n, m; while(scanf("%d%d", &n, &m) == 2) { build(1, 1, n); while(m--) { int op, x, y; scanf("%d", &op); if(op == 1) { scanf("%d", &y); if(sub[1] < y) { printf("0\n"); continue; } int p = query(y, root); printf("%d\n", p); update(p, p + y - 1, 1, root); } else { scanf("%d%d", &x, &y); update(x, x + y - 1, 0, root); } } } return 0; }