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; }
完整代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 */