zoukankan      html  css  js  c++  java
  • poj3667(线段树区间合并&区间查询)

    题目链接: http://poj.org/problem?id=3667

    题意:第一行输入 n, m表示有 n 间房间(连成一排的), 接下来有 m 行输入, 对于接下来的 m 行输入:

    1 x : 询问是否有长度为 x 的连号空房, 若有, 住进最左边并输出对应编号;

    2 x y : 将区间 [x, x + y - 1] 的房间清空;

    思路: 并查集区间合并&区间查询

    下面一段题解摘自: http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html

    1 查询操作,找一段长度为W的没被覆盖的最左的区间
    2 更新操作,将某段连续的区域清空


    更新操作相对容易解决,关键是怎么实现查询操作
    既然是要找一段长度至少为W的区间,要做到这点,其实不难,我们可以在每个线段树的节点里增加一个域tlen,表示该区间可用的区间的最大长度,
    至于这个tlen区间的具体位置在哪里不知道,只是知道该区间内存在这么一段可用的区间,并且注意,这个tlen表示的是最大长度,该节点可能有多段可用的区间,但是最长的长度是tlen
    记录了这个信息,至少能解决一个问题,就是能不能找到一个合适的区间。如果查询的区间长度W > 总区间的tlen,那么查询一定是失败的(总区间中可以的最大区间都不能满足那就肯定失败)
    但这远远不够,其一查询是要返回区间的具体位置的,这里无法返回位置,另外是要查询最左区间,最左的且满足>=W的区间可能不是这个tlen区间

    那么我们进一步思考这个问题
    首先我们先增加两个域,llen,rlen
    llen表示一个区间从最左端开始可用的且连续的最大长度
    例如区间[1,5],覆盖情况为[0,0,0,1,1],llen = 3,从最左端有3格可以利用
    区间[1,5],覆盖情况为[1,0,0,0,0],llen = 0,因为从最左端开始找不到1格可用的区间
    rlen表示一个区间从最右端开始可用的且连续的最大长度
    例如区间[1,5],覆盖情况为[1,0,1,0,0],rlen = 2,从最右端有2格可以利用
    区间[1,5],覆盖情况为[0,0,0,0,1],rlen = 0,因为从最右端开始找不到1格可用的区间
    对于一个区间,我们知道它左半区间的tlen,和右半区间的tlen,如果左半区间的tlen >= W ,那么我们一定能在左边找到(满足最左),所以可以深入到左半区间去确定该区间的具体位置
    如果左端的不满足,那么我们要先考虑横跨两边的区间(因为要满足最左),因而记录的llen,rlen可以派上用场,一段横跨的区间,
    那么是 左边区间rrlen + 右边区间llen ,如果满足的话,就是该区间了,它的位置也是可以确定的
    如果横跨的区间不满足,那么就在右半区间找,如果右半区间的tlen >= W , 那么可以在右半区间找到,所以深入到右半区间去确定它的具体位置,否则的话,整个查询就失败了

    可见查询是建立在tlen,llen,rlen这个信息之上的,而每次查询后其实伴随着修改,而且还有专门的修改操作,这些修改操作都会改变tlen,llen,rlen的值,所以在更新的时候是时刻维护这些信息

    关于这3个信息的维护

    当前区间的tlen = max{ 左半区间tlen , 右半区间tlen , 左半区间rlen+右半区间llen} (这个不难理解吧,取左右较大的那个,或者横跨中间的那个)

    如果左半区间全部可以用: 当前区间llen = 左半区间llen(tlen) + 右半区间llen
    左半区间部分能用: 当前区间llen = 左半区间llen

    如果右半区间全部能用: 当前区间rlen = 右半区间rlen(tlen) + 左半区间rlen
    右半区间部分能用: 当前区间rlen = 右半区间rlen

    这样就全部维护好了

    代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #define lson l, mid, rt << 1
     4 #define rson mid + 1, r, rt << 1 | 1
     5 using namespace std;
     6 
     7 const int MAXN = 5e4 + 10;
     8 int msum[MAXN << 2], lsum[MAXN << 2], rsum[MAXN << 2], cover[MAXN << 2];
     9 
    10 void push_down(int rt, int m){//将本层标记移到下一层
    11     if(cover[rt] != -1){
    12         cover[rt << 1] = cover[rt << 1 | 1] = cover[rt];
    13         msum[rt << 1] = lsum[rt << 1] = rsum[rt << 1] = cover[rt] ? 0 : m - (m >> 1);
    14         msum[rt << 1 | 1] = lsum[rt << 1 | 1] = rsum[rt << 1 | 1] = cover[rt] ? 0 : (m >> 1);
    15         cover[rt] = -1;
    16     }
    17 }
    18 
    19 void push_up(int rt, int m){//向上更新一层
    20     lsum[rt] = lsum[rt << 1];
    21     rsum[rt] = rsum[rt << 1 | 1];
    22     if(lsum[rt] == m - (m >> 1)) lsum[rt] += lsum[rt << 1 | 1];
    23     if(rsum[rt] == (m >> 1)) rsum[rt] += rsum[rt << 1];
    24     msum[rt] = max(lsum[rt << 1 | 1] + rsum[rt << 1], max(msum[rt << 1], msum[rt << 1 | 1]));
    25 }
    26 
    27 void build(int l, int r, int rt){//建树
    28     msum[rt] = lsum[rt] = rsum[rt] = r - l + 1;
    29     cover[rt] = -1;
    30     if(l == r) return;
    31     int mid = (l + r) >> 1;
    32     build(lson);
    33     build(rson);
    34 }
    35 
    36 void update(int L, int R, int x, int l, int r, int rt){//更新
    37     if(L <= l && R >= r){
    38         msum[rt] = lsum[rt] = rsum[rt] = x ? 0 : r - l + 1;//x不为0即rt对应区间已住人
    39         cover[rt] = x;//lazy标记
    40         return;
    41     }
    42     push_down(rt, r - l + 1);//向下更新
    43     int mid = (l + r) >> 1;
    44     if(L <= mid) update(L, R, x, lson);
    45     if(R > mid) update(L, R, x, rson);
    46     push_up(rt, r - l + 1);//向上更新
    47 }
    48 
    49 int query(int x, int l, int r, int rt){//查询
    50     if(l == r) return l;
    51     push_down(rt, r - l + 1);
    52     int mid = (l + r) >> 1;
    53     if(msum[rt << 1] >= x) return query(x, lson);
    54     else if(lsum[rt << 1 | 1] + rsum[rt << 1] >= x) return mid - rsum[rt << 1] + 1;
    55     return query(x, rson);
    56 }
    57 
    58 int main(void){
    59     int n, m, op, x, y;
    60     scanf("%d%d", &n, &m);
    61     build(1, n, 1);
    62     while(m--){
    63         scanf("%d", &op);
    64         if(op == 1){
    65             scanf("%d", &x);
    66             if(msum[1] < x){
    67                 printf("0
    ");
    68                 continue;
    69             }
    70             int cnt = query(x, 1, n, 1);
    71             printf("%d
    ", cnt);
    72             update(cnt, cnt + x - 1, 1, 1, n, 1);
    73         }else{
    74             scanf("%d%d", &x, &y);
    75             update(x, x + y - 1, 0, 1, n, 1);
    76         }
    77     }
    78     return 0;
    79 }
    View Code
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    现在使用控件, 更喜欢继承(覆盖控件已有的函数,很奇怪的一种使用方式)
    Controls 属性与继承 TShape 类的小练习(使用TShape可以解决很多图形问题)
    QT创建窗口程序、消息循环和WinMain函数(为主线程建立了一个QEventLoop,并执行exec函数)
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7020386.html
Copyright © 2011-2022 走看看