只保证自己看得懂。
听说与普通线段树相比,zkw发明的这玩意儿更快、更短OrzOrzOrz,那就学吧。
1. 建树
设数组大小为 (n) ,叶结点个数为 (N) 。
zkw线段树为一颗满二叉树,故 (N = 2^{leftlceillog_2 n
ight
ceil}) 。
建树可以这么写:
N = 1;
while (N < n) N <<= 1;
for (int i = N; i < N + n; ++i)
s[i] = Read();
for (int i = N - 1; i; --i)
s[i] = s[i << 1] + s[i << 1 | 1];
2. 单点修改+区间查询
只说区间查询,设区间为 ([x,y]) ,令 (l = N + x - 1,r = N + y - 1) 。
先令 (ans gets s_l + s_r) 。
然后不断进行下面过程:
- 判断 (l) 与 (r) 父亲是否相同,相同时 (l oplus r = 1) 。
- 若 (l) 为其父亲的左儿子,则 (ans) 加上其父亲右儿子的值。
若 (r) 为其父亲的右儿子,则 (ans) 加上其父亲左儿子的值。 - 令 (l getsleftlfloordfrac{l}{2} ight floor,rgets leftlfloordfrac{r}{2} ight floor) 。
直接放代码了。
例题:Luogu P3374
#include <cstdio>
#include <cctype>
#define MAX_N (500000 + 5)
int n, N = 1, m;
long long s[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)
s[i] = Read();
for (int i = N - 1; i; --i)
s[i] = s[i << 1] + s[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)
s[i] += y;
} else {
ans = s[N + x - 1] + s[N + y - 1];
for (int l = N + x - 1, r = N + y - 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ans += s[l ^ 1];
if (r & 1) ans += s[r ^ 1];
}
printf("%lld
", ans);
}
}
return 0;
}
3. 区间修改+区间查询
加上标记永久化即可。
即每次操作要修改自己的父亲,如果 (l,r) 父亲不同还可能要修改父亲的左(右)儿子。
对着上一个区间查询来改就好了。
注意这里 (l,r) 要一直上传直到父亲为根结点。
也是直接放代码了。
例题:Luogu P3372
#include <cstdio>
#include <cctype>
#define MAX_N (100000 + 5)
typedef long long LL;
int n, N = 1, m;
LL s[MAX_N * 3], 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)
s[i] = Read();
for (int i = N - 1; i; --i)
s[i] = s[i << 1] + s[i << 1 | 1];
int opt, x, y, k;
LL ans;
while (m--) {
opt = Read(), x = Read(), y = Read();
if (opt == 1) {
k = Read();
t[N + x - 1] += k;
if (x ^ y) {
t[N + y - 1] += k;
}
for (int l = N + x - 1, r = N + y - 1, lcnt = 1, rcnt = 1, cnt = 1; l ^ 1; l >>= 1, r >>= 1, cnt <<= 1) {
if (l ^ r && l ^ r ^ 1) {
if (~l & 1) {
t[l ^ 1] += k;
lcnt += cnt;
}
if (r & 1) {
t[r ^ 1] += k;
rcnt += cnt;
}
s[l >> 1] += (LL)k * lcnt;
s[r >> 1] += (LL)k * rcnt;
} else {
s[l >> 1] += (LL)k * (y - x + 1);
}
}
} else {
if (x ^ y) ans = s[N + x - 1] + s[N + y - 1] + t[N + x - 1] + t[N + y - 1];
else ans = s[N + x - 1] + t[N + x - 1];
for (int l = N + x - 1, r = N + y - 1, lcnt = 1, rcnt = 1, cnt = 1; l ^ 1; l >>= 1, r >>= 1, cnt <<= 1) {
if (l ^ r && l ^ r ^ 1) {
if (~l & 1) {
ans += s[l ^ 1] + t[l ^ 1] * cnt;
lcnt += cnt;
}
if (r & 1) {
ans += s[r ^ 1] + t[r ^ 1] * cnt;
rcnt += cnt;
}
ans += t[l >> 1] * lcnt + t[r >> 1] * rcnt;
} else {
ans += t[l >> 1] * (y - x + 1);
}
}
printf("%lld
", ans);
}
}
return 0;
}