zkw线段树 小记(咕咕咕)
作者:互联网
只保证自己看得懂。
1. 建树
设数组大小为 \(n\) ,叶结点个数为 \(N\) 。
zkw线段树为一颗满二叉树,故 \(N = 2^{\left\lceil\log_2 n\right\rceil}\) 。
建树可以这么写:
N = 1;
while (N < n) N <<= 1;
for (int i = N; i < N + n; ++i)
t[i] = Read();
for (int i = N - 1; i; --i)
t[i] = t[i << 1] + t[i << 1 | 1];
2. 单点修改+区间查询
只说区间查询,设区间为 \([x,y]\) ,令 \(l = N + x - 1,r = N + y - 1\) 。
先令 \(ans \gets t_l + t_r\) 。
然后不断进行下面过程:
- 判断 \(l\) 与 \(r\) 父亲是否相同,相同时 \(l \oplus r = 1\) 。
- 若 \(l\) 为其父亲的左儿子,则答案加上其父亲右儿子的值。
若 \(r\) 为其父亲的右儿子,则答案加上其父亲左儿子的值。 - 令 \(l = \left\lfloor\dfrac{l}{2}\right\rfloor,r= \left\lfloor\dfrac{r}{2}\right\rfloor\) 。
直接放代码了。
例题:Luogu P3374
#include <cstdio>
#include <cctype>
#define MAX_N (500000 + 5)
int n, N = 1, m;
long long t[MAX_N * 3];
int Read() {
int res = 0, sign = 0;
char ch = getchar();
while (!isdigit(ch)) sign |= ch == '-', ch = getchar();
while (isdigit(ch)) res = res * 10 + ch - '0', ch = getchar();
return sign ? -res : res;
}
int main() {
n = Read(), m = Read();
while (N < n) N <<= 1;
for (int i = N; i < N + n; ++i)
t[i] = Read();
for (int i = N - 1; i; --i)
t[i] = t[i << 1] + t[i << 1 | 1];
int opt, x, y;
long long ans;
while (m--) {
opt = Read(), x = Read(), y = Read();
if (opt == 1) {
for (int i = N + x - 1; i; i >>= 1)
t[i] += y;
} else {
ans = t[N + x - 1] + t[N + y - 1];
for (int l = N + x - 1, r = N + y - 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ans += t[l ^ 1];
if (r & 1) ans += t[r ^ 1];
}
printf("%lld\n", ans);
}
}
return 0;
}
3. 区间修改+区间查询
标签:ch,int,res,线段,while,right,ans,zkw,小记 来源: https://www.cnblogs.com/kcn999/p/13762420.html