zkw线段树解决区间rmq

原创 2014年05月22日 23:47:48

zkw线段树具体内容请百度统计的力量(这是他讲的时候所用的ppt的名字)

今天我们就来完整的写一个zkw线段树。

正如他在ppt里讲的

*差分是化绝对为相对的重要手段
*标记永久化后就是值,只不过这种值是相对的
*计算答案时可以利用从节点到根部的信息

在zkw树中,每个节点存的都不是最大值M[x], 而是M[x] - M[x<<1](这里M[x<<1]就是他父亲存在最大值)。这样有什么用呢?你可以试一下从某个结点开始,走到根,你会发现一路上的和加起来,就是这里存在最大值。这样,单点Query就解决了。单点更新也比较简单,就是一点一点从叶子结点处开始修改,维护这棵树作为差分树的性质。只这样看的话,并不比普通的线段树有多大优势。

所以接下来我们看区间更新和区间查询。

区间更新时,由于是整个区间都变化,这就造成什么了呢,如果有两个叶子节点都在要更新的区间里,他俩父亲相同,而且他俩也非端点,这时,由于他俩是一起更新的,你会发现这时他俩的相对差值仍为变化,而且,由于他们自身存的是差分值,而且他们的最大值也同等增加了相同值,所以这俩个节点存的值并不需要改变,需要改变的是哪里呢?就是边界节点和跟边界节点通父亲的点,这样底层的更新最多4个点。解决完底层,它的上一层也是同理,从而每层都最多只要四个点。当左右边界同父亲的时候,在往上都只要更新两个点。这时在和线段树比较的话,你会发现,更新的点的个数少了很多有木有,当更新的层数越来越深的时候,zkw树至多4k个点,而普通线段树则是2^k个点左右。这时赤裸裸的差距啊有木有。

至于区间查询,由于每个点存的内容都和它的儿子没有关系,所以某种意义上我们可以直接找到区间的边界点,然后开始一层一层向上累加去个最大or最小就可以了。但是这个找边界点的过程还会浪费点地方。不如直接就从边界的叶子节点处一层一层向上累加,每到一层,都注意去最大or最小,来维护答案。


然后就是他的ppt中的错误,第一处在add函数里,它没有上浮到根节点,这样的话单就下次查询来说,并不会造成什么(你可以试试,这是从某点一直到根的路径中的sum仍为这个点存的Max),但是,如果有了另一次更新,这时,就会出现问题(出个数据模拟一下就可以)。所以在add函数的尾部还要加一部上浮到根节点的语句。

第二处在Query函数中,如果这时查询的不是区间是点,这时会有l=r,进一次循环,你发现l=r=0,这时,很明显,函数最后的那步上浮到根节点的循环是出不去了。

第三处仍在Query函数中,那就是这时的查询不再是开区间了,而是闭区间。比如区间的左节点是某个点的右儿子,那么这时的开区间头便是某个点的左儿子。这时按照zkw的惯例,下次的开区间节点就是他的父亲了,但是由于他的父亲的儿子中有区间内的点,从而这个父亲也算区间内的点,那么这时会出现了矛盾,也就是说,本来要经历的点却没有经历。这样肯定就有问题了,所以我们只能闭区间查询,这样才不会发生上浮一层之后,点与区间的位置关系发生改变。


好了,这次的总结就差不多了。这里是cf上的一个题目,考的就是这个。底下附上我的代码,可以看一下细节部分。

#include <bits/stdc++.h>

#define up(i, lower, upper) for(int i = lower; i < upper; i++)
#define down(i, lower, upper) for(int i = upper-1; i >= lower; i--)

using namespace std;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef vector<int> vi;
typedef vector<pii> vpii;
typedef __int64 ll;
typedef unsigned __int64 ull;

const double pi = acos(-1);
const double eps = 1.0e-9;

template<class T>
inline char read(T &n){
    T x = 0, tmp = 1; char c = getchar();
    while((c < '0' || c > '9') && c != '-' && c != EOF) c = getchar();
    if(c == '-') c = getchar(), tmp = -1;
    while(c >= '0' && c <= '9') x *= 10, x += (c - '0'),c = getchar();
    n = x*tmp;
    return c;
}

template <class T>
inline void write(T n) {
    if(n < 0) {
        putchar('-');
        n = -n;
    }
    int len = 0,data[20];
    while(n) {
        data[len++] = n%10;
        n /= 10;
    }
    if(!len) data[len++] = 0;
    while(len--) putchar(data[len]+48);
}

//---------------------------------------------------------
struct SegTree {
    ll tree[550000];
    int m;

    void build(int len) {
        m = 1;
        memset(tree, 0, sizeof tree);
        while(m-1 <= len) m*=2;
        up(i, 1, len+1) read(tree[i+m]);
        down(i, 1, m) tree[i] = min(tree[(i<<1)], tree[(i<<1)+1]);
        down(i, 1, len+m+1) tree[i] = tree[i] - tree[i>>1];
    }

    void update(int l, int r, int val) {
        ll tmp;
        for(l += m-1, r += m+1; l^r^1; l>>=1, r>>=1) {
            if(~l&1) tree[l^1]+=val;
            if(r&1) tree[r^1]+=val;
            tmp = min(tree[l], tree[l^1]), tree[l] -= tmp, tree[l^1] -= tmp, tree[l>>1] += tmp;
            tmp = min(tree[r], tree[r^1]), tree[r] -= tmp, tree[r^1] -= tmp, tree[r>>1] += tmp;
        }
        for (;l!=1;l>>=1)
            tmp = min(tree[l],tree[l^1]), tree[l] -= tmp, tree[l^1] -= tmp, tree[l>>1] += tmp;
    }

    ll query(int l, int r) {
        ll lAns = 0, rAns = 0;
        l+=m, r+=m;
        if(l != r) {
            for(; l^r^1; l>>=1, r>>=1) {
                lAns += tree[l], rAns += tree[r];
                if(~l&1) lAns = min(lAns, tree[l^1]);
                if(r&1) rAns = min(rAns, tree[r^1]);
            }
        }
        ll ans = min(lAns+tree[l], rAns+tree[r]);
        while(l > 1) ans += tree[l>>=1];
        return ans;
    }
};

SegTree a;

int main() {
    int len, n, m, l, r;
    read(len);
    a.build(len);
    read(n);
    while(n--) {
        read(l);
        if(read(r) != '\n') {
            read(m);
            if(l > r) a.update(l+1, len, m), a.update(1, r+1, m);
            else a.update(l+1, r+1, m);
        }
        else {
            if(l > r) write(min(a.query(l+1, len), a.query(1, r+1)));
            else write(a.query(l+1, r+1));
            puts("");
        }
    }
    return 0;
}


数据结构(C版)

本课程是计算机专业的一个基础课程。 内容涉及线性表的存储和操作,二叉树的存储和操作,图的存储和操作,数据的查找和排序的算法。
  • 2015年03月26日 22:31

zkw线段树详解

转载自:http://blog.csdn.net/qq_18455665/article/details/50989113 前言 首先说说出处:清华大学 张昆玮(zkw) - ppt 《统计的力...
  • keshuqi
  • keshuqi
  • 2016-08-14 19:35:10
  • 6347

非递归式(zkw)线段树详解(一)

线段树,是在信息学各类比赛中经常出现的数据结构之一 普通的线段树大家应该都会,下面来介绍一种不需要递归的线段树zkw线段树 出自:《统计的力量》-张昆玮zkw线段树不同于普通线段树的地方在于:它采...
  • Fop_zz
  • Fop_zz
  • 2017-02-17 15:13:33
  • 1666

zkw线段树

zkw线段树
  • unicornt_
  • unicornt_
  • 2016-07-31 14:05:28
  • 536

良心!良心!十分良心的zkw线段树(更新中)

zkw线段树
  • mk703
  • mk703
  • 2017-01-25 11:46:08
  • 549

ZKW线段树

题目:敌兵布阵   题意:给一个数组,然后有一系列操作:(1)把某一个值加上一个数,(2)把某一个值减去一个数,(3)求一段区间的和。 #include #include #include u...
  • ACdreamers
  • ACdreamers
  • 2013-08-31 10:41:00
  • 5805

zkw线段树的理解和思考

zkw线段树
  • u012513980
  • u012513980
  • 2014-05-21 20:31:23
  • 2392

非递归ZKW线段树完全模板

经过一番刷题,终于用非递归线段树搞定线段树的基本问题,ZKW神牛的《统计的力量》带我入门,尽管后面一大堆都看不懂……言归正传,贴模板,顺便为以后区域赛做准备——int M, T; /* M和T都是非递...
  • forget311300
  • forget311300
  • 2015-03-16 14:44:30
  • 1845

HDU 1754 单点更新 区间求和 zkw线段树 + 递归线段树

HDOJ 1754 线段树裸题,直接上代码。 AC code: 递归线段树: //lrl's submission #include #include #inc...
  • ADjky
  • ADjky
  • 2016-09-14 13:03:01
  • 378

zkw线段树 模板

自己被网络流狂虐,所以就写数据结构去了。。。 下面这个模板还是罗雨屏神犇写的。。。我加了点注释。 #include #include #include #define m (1>=1)//锁定叶...
  • SAI_D
  • SAI_D
  • 2015-02-12 16:25:05
  • 541
收藏助手
不良信息举报
您举报文章:zkw线段树解决区间rmq
举报原因:
原因补充:

(最多只允许输入30个字)