其他分享
首页 > 其他分享> > zkw线段树 小记(咕咕咕)

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\) 。
然后不断进行下面过程:

  1. 判断 \(l\) 与 \(r\) 父亲是否相同,相同时 \(l \oplus r = 1\) 。
  2. 若 \(l\) 为其父亲的左儿子,则答案加上其父亲右儿子的值。
    若 \(r\) 为其父亲的右儿子,则答案加上其父亲左儿子的值。
  3. 令 \(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