zkw线段树

zkw线段树是对普通线段树的常熟优化版本,$≈$树状数组的常数。

同时普通线段树是近似完全二叉树,而zkw线段树是满二叉树,且普通线段树自上而下访问,zkw线段树先找到叶子节点,自下而上进行访问。

那么易得建树

void Build () {
    for (M = 1; M <= N + 1; M <<= 1);
    
    for (int i = M + 1; i <= M + N; i ++)
        Tree[i].val = A[i - M];
    for (int i = M - 1; i >= 1; i --)
        Tree[i].val = Tree[i << 1].val + Tree[i << 1 | 1].val;
}

对于区间修改或查询,我们假定此时区间为$[L, R]$,那么令$s = L - 1$,$t = R + 1$(需找到叶子节点编号),同时向上跳,且满足:若$s$所在为左子节点,则修改其右子节点,反之不进行操作;同理若t所在为右子节点,则修改其左子节点,反之不进行操作,并且结束条件为s与t成为兄弟。

可以发现,依据这样的规则$s$和$t$会遍历普通线段树上所有需进行修改的节点,并且冗余的节点是不会被修改访问到的。

那么先进行较简单的区间加减,及区间求和操作。

对于无法下传的懒标记,令其为永久性标记即可。

void Modify (int L, int R, LL k) {
    LL lnum = 0, rnum = 0, nnum = 1; // 记录已覆盖叶子节点数(即原序列上的节点数)
    int s, t;
    for (s = L + M - 1, t = R + M + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nnum <<= 1) { // s ^ t ^ 1用于判断父节点是否相同
        Tree[s].val += lnum * k; // 覆盖这一半应当覆盖的区间(此处只会修改到线段树中覆盖了当前区间的节点,因为否则lnum / rnum就会为0)
        Tree[t].val += rnum * k;
        
        if (~ s & 1) {
            Tree[s ^ 1].val += nnum * k; // 修改另一半应当覆盖的区间
            Tree[s ^ 1].lazy += k; // 打永久性懒标记
            lnum += nnum;
        }
        if (t & 1) {
            Tree[t ^ 1].val += nnum * k;
            Tree[t ^ 1].lazy += k;
            rnum += nnum;
        }
    }
    
    for (; s; s >>= 1, t >>= 1) { // 剩余祖先处的修改
        Tree[s].val += k * lnum;
        Tree[t].val += k * rnum;
    }
}

区间求和大致一致。

LL Query (int L, int R) {
    LL ans = 0;
    LL lnum = 0, rnum = 0, nnum = 1;
    int s, t;
    for (s = L + M - 1, t = R + M + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nnum <<= 1) {
        if (Tree[s].lazy)
            ans += Tree[s].lazy * lnum; // 如果不在路径上,lnum / rnum为0,不会加上冗余节点
        if (Tree[t].lazy)
            ans += Tree[t].lazy * rnum;
        
        if (~ s & 1) {
            ans += Tree[s ^ 1].val; // 此处要加因为访问不到
            lnum += nnum;
        }
        if (t & 1) {
            ans += Tree[t ^ 1].val;
            rnum += nnum;
        }
    }
    
    for (; s; s >>= 1, t >>= 1) { // 剩余节点处理
        if (Tree[s].lazy)
            ans += Tree[s].lazy * lnum;
        if (Tree[t].lazy)
            ans += Tree[t].lazy * rnum;
    }
    
    return ans;
}

完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

typedef long long LL;

const int MAXN = 1e05 + 10;

struct Node {
    LL val;
    LL lazy;
    
    Node () {
        val = 0;
        lazy = 0;
    }
} ;

Node Tree[MAXN << 2];

int N, Q, M;

LL A[MAXN]; 

void Build () {
    for (M = 1; M <= N + 1; M <<= 1);
    
    for (int i = M + 1; i <= M + N; i ++)
        Tree[i].val = A[i - M];
    for (int i = M - 1; i >= 1; i --)
        Tree[i].val = Tree[i << 1].val + Tree[i << 1 | 1].val;
}

void Modify (int L, int R, LL k) {
    LL lnum = 0, rnum = 0, nnum = 1;
    int s, t;
    for (s = L + M - 1, t = R + M + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nnum <<= 1) {
        Tree[s].val += lnum * k;
        Tree[t].val += rnum * k;
        
        if (~ s & 1) {
            Tree[s ^ 1].val += nnum * k;
            Tree[s ^ 1].lazy += k;
            lnum += nnum;
        }
        if (t & 1) {
            Tree[t ^ 1].val += nnum * k;
            Tree[t ^ 1].lazy += k;
            rnum += nnum;
        }
    }
    
    for (; s; s >>= 1, t >>= 1) {
        Tree[s].val += k * lnum;
        Tree[t].val += k * rnum;
    }
}

LL Query (int L, int R) {
    LL ans = 0;
    LL lnum = 0, rnum = 0, nnum = 1;
    int s, t;
    for (s = L + M - 1, t = R + M + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nnum <<= 1) {
        if (Tree[s].lazy)
            ans += Tree[s].lazy * lnum;
        if (Tree[t].lazy)
            ans += Tree[t].lazy * rnum;
        
        if (~ s & 1) {
            ans += Tree[s ^ 1].val;
            lnum += nnum;
        }
        if (t & 1) {
            ans += Tree[t ^ 1].val;
            rnum += nnum;
        }
    }
    
    for (; s; s >>= 1, t >>= 1) {
        if (Tree[s].lazy)
            ans += Tree[s].lazy * lnum;
        if (Tree[t].lazy)
            ans += Tree[t].lazy * rnum;
    }
    
    return ans;
}

LL getnum () {
    LL num = 0;
    char ch = getchar ();
    
    while (ch < '0' || ch > '9')
        ch = getchar ();
    while (ch >= '0' && ch <= '9')
        num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
    
    return num;    
}

int main () {
    N = getnum (), Q = getnum ();
    
    for (int i = 1; i <= N; i ++)
        A[i] = getnum ();
    
    Build ();
    
    for (int i = 1; i <= Q; i ++) {
        int opt;
        opt = getnum ();
        
        if (opt == 1) {
            int x, y, k;
            x = getnum (), y = getnum (), k = getnum ();
            
            Modify (x, y, k);
        }
        else {
            int x, y;
            x = getnum (), y = getnum ();
            
            LL ans = Query (x, y);
            
            printf ("%lld
", ans);
        }
    }
    
    return 0;
}

/*
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
*/
zkw线段树(区间加、区间和)
原文地址:https://www.cnblogs.com/Colythme/p/9823129.html