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;
}


#bzoj3187#区间修改的RMQ问题(zkw线段树版)

区间修改的RMQ问题 时间限制: 1 Sec  内存限制: 128 MB 题目描述 给出N(1 ≤ N ≤ 50,000)个数的序列A,下标从1到N,每个元素值均不超过1000。有两...
  • its_elaine
  • its_elaine
  • 2017年03月11日 10:47
  • 386

ZKW线段树

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

zkw线段树,区间修改,最值查询(差分)

#include #include #include #include #include #include #include using namespace std; const int N=5001...
  • LELOUCH_YUKI
  • LELOUCH_YUKI
  • 2017年03月11日 08:49
  • 584

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

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

zkw线段树 运用

poj 3468 经典的区间修改和区间和查询,需要灵活运用差分和前缀和。 设A为原数组,A'为差分数组(A'i:=Ai-A[I-1])。 那么,我们如果要求Ai,便可从A'1+...A'I得出,...
  • huyuncong
  • huyuncong
  • 2011年05月23日 22:28
  • 3243

数据结构--zkw线段树

好几天没写了,写点儿奇怪的东西,一个不好理解的黑科技。zkw线段树,顾名思义,就是zkw大神发明的线段树。 由于我实在是太弱了,无法讲述zkw大神的高深的ppt,就留一个下载网址:统计的力量(zkw...
  • stone41123
  • stone41123
  • 2017年08月19日 00:15
  • 112

zkw线段树详解

前言 首先说说出处:清华大学 张昆玮(zkw) - ppt 《统计的力量》本文(辣鸡)编辑:BeiYu写这篇博客的原因:  1.zkw线段树非递归,效率高,代码短  2.网上关于zkw线段树...
  • coutamg
  • coutamg
  • 2017年04月20日 10:25
  • 815

zkw线段树 标记下放

zkw线段树的标记上传好像只能做区间修改的rmq,但标记下放应用面就与朴素线段树差不多了,但是漂亮很多。 在进行修改或询问时,我们先将当前区间的标记全部下放,从左右开区间端点从顶向下跑一边下放就是,...
  • huyuncong
  • huyuncong
  • 2011年10月23日 09:10
  • 1948

zkw线段树详解

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

zkw线段树的理解和思考

zkw线段树
  • u012513980
  • u012513980
  • 2014年05月21日 20:31
  • 2281
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:zkw线段树解决区间rmq
举报原因:
原因补充:

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