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\n", 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
*/
来源:https://www.cnblogs.com/Colythme/p/9823129.html
