poj 3667 线段树前后缀区间 Hotel

来源:互联网 发布:java分布式锁原理 编辑:程序博客网 时间:2024/05/27 20:07
【题意】

一共有n个空位,初始全为空,会有两种操作,一种是询问是否有r个连续的空位,若有,则将最左端有r个连续的空位的地方全部填满,若没有则不填,另一种是将从u开始的d个位置全部清空

【输入】

第一行两个数n、m(<=50000),表示n个空位,m次操作

接下来m行每行表示一次操作,

若该行第一个数字为1,接下来是一个数字,表示操作一

若该行第一个数字为2,接下来是两个数字,表示操作二

【输出】

对于每个操作一,若存在r个连续的空位,则输出最靠左的r个连续空位的起点,若不存在则输出0

解:

      用lsum表示当前区间最长连续前缀的长度,rsum表示当前区间最长连续后缀的长度,msum表示本个区间最长连续段的长度。重点是还有一个cover,-1表示这个区间既有非空的,又有空的; 1 表示整个当前区间都满了; 0 表示整个区间都空了。 就是lazy。 

     查询的时候,首先,得满足有这样连续的长度。其次:

                            如果当前区间前缀长度就大于等于我们想要的长度的话,就返回左端点。 

                            如果左孩子的最长长度>=wanted的话,就去左孩子里面找。

                            如果包含m的连续区间>=wanted的话,就直接返回左孩子后缀的最左端。

                            不然,就只能去右孩子里面找了。

做题过程:

        开始没有cover,没有push_dn,就只有一个push_up。 想来更新的时候不是没有更新到底吗,也就是说孩子还没更新呢,所以一定得有cover的呀。

        现在再一想,果然表示当前区间是线段树的根本,我怎么给忘了呢。。。

/*Pro: 0Sol:date:*/#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cmath>#include <queue>#include <set>#include <vector>#define maxn 50010#define lson l,m , rt << 1#define rson m + 1, r , rt << 1 | 1#define ls rt << 1#define rs rt << 1 | 1int n,Q,a,b,op;using namespace std;int lsum[maxn << 2], rsum[maxn << 2], msum[maxn << 2], cover[maxn << 2];void build(int l, int r, int rt){    lsum[rt] = rsum[rt] = msum[rt] = r - l + 1;    cover[rt] = 0;//    if(l == r) return ;    int m = (l + r) >> 1;    build(lson); build(rson);}void push_dn(int rt, int m){    if(cover[rt] != -1){        cover[ls] = cover[rs] = cover[rt];        lsum[ls] = rsum[ls] = msum[ls] = cover[rt] == 0? m - (m >> 1): 0;//        lsum[rs] = rsum[rs] = msum[rs] = cover[rt] == 0? (m >> 1): 0;        cover[rt] = -1;    }}void push_up(int rt,int m){    lsum[rt] = lsum[ls];    rsum[rt] = rsum[rs];    if(lsum[ls] == m - (m >> 1)) lsum[rt] += lsum[rs];    if(rsum[rs] == m >> 1) rsum[rt] += rsum[ls];    msum[rt] = max(msum[ls] , msum[rs]);    msum[rt] = max(msum[rt], rsum[ls] + lsum[rs]);}//int query(int dis, int l, int r, int rt){//notOnlySuccess 的版本, 有点不是很懂。。。//    if(l == r) return l;////    push_dn(rt, r - l + 1);//    int m = (l + r) >> 1;//    if(msum[ls] >= dis) return query(dis,lson);//    if(rsum[ls] + lsum[rs] >= dis) return m - rsum[ls] + 1;//    return query(dis, rson);//}int query(int dis, int l, int r, int rt){//寻找长度为dis的最左边的区间的左端点,于是就自己借鉴着改了一改,A了    if(lsum[rt] >= dis) return l;    push_dn(rt, r - l + 1);    int m = (l + r) >> 1;    if(msum[ls] >= dis) return query(dis,lson);    if(rsum[ls] + lsum[rs] >= dis) return m - rsum[ls] + 1;    return query(dis, rson);}void update(int L,int R, int sign, int l, int r, int rt){    if(L <= l && r <= R){        lsum[rt] = rsum[rt] = msum[rt] = sign == 0 ? r - l + 1 : 0;        cover[rt] = sign;        return ;    }push_dn(rt,r - l + 1);    int m = (l + r) >> 1;    if(L <= m) update(L,R,sign,lson);    if(R > m) update(L,R,sign,rson);    push_up(rt,r - l + 1);//}int main(){    while(~scanf("%d%d",&n,&Q)){        build(1,n,1);        for(int i = 0;  i < Q; i ++){            scanf("%d",&op);            if(op == 1){                scanf("%d",&a);                if(msum[1] < a) puts("0");                else{                    int st = query(a,1,n,1);                    printf("%d\n", st);                    update(st, st + a - 1, 1 ,1, n, 1);                }            }else{                scanf("%d%d",&a,&b);                update(a, a +  b - 1, 0, 1, n, 1);//0 表示没住人            }        }    }return 0;}


原创粉丝点击