zkw线段树 小记

只保证自己看得懂。


听说与普通线段树相比,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)
然后不断进行下面过程:

  1. 判断 (l)(r) 父亲是否相同,相同时 (l oplus r = 1)
  2. (l) 为其父亲的左儿子,则 (ans) 加上其父亲右儿子的值。
    (r) 为其父亲的右儿子,则 (ans) 加上其父亲左儿子的值。
  3. (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;
}
原文地址:https://www.cnblogs.com/kcn999/p/13762420.html