关于zkw式线段树,请参考zkw大牛的ppt--百度文库《统计的力量》,其特点是利用完全二叉树数组存储的特点,自底向上遍历节点,非递归地完成查询和更新,加上位操作的使用,可以提高程序的效率。ps:__int64比long long的效率高很多。
#include <stdio.h> //#define DEBUG #ifdef DEBUG #define debug(...) printf( __VA_ARGS__) #else #define debug(...) #endif #define N 100002 struct tree_node{ int s; int t; long long sum; /* A[s]+...+A[t]的部分和 */ long long d; /* A[s]...A[t]的公共增量 */ }; struct tree_node tree[262144]; int A[N]; int M; /* M表示线段树中叶子节点的个数, 对于完全二叉树,M是2的n次幂 */ /* 对[0,M-1]建造线段树,M是2的n次幂,保证了这是一棵完全二叉树 */ void make_tree(int s, int t) { int i; for (i = 2*M-1; i > 0; i--) { if (i >= M) { /* 叶子节点 */ tree[i].sum = A[i-M]; tree[i].s = tree[i].t = (i-M); } else { /* 分支节点 */ tree[i].sum = tree[i<<1].sum + tree[(i<<1)+1].sum; tree[i].s = tree[i<<1].s; tree[i].t = tree[(i<<1)+1].t; } } } /* 区间(s,t)的和等于线段<s,t>的所有匹配线段之和,加上每条匹配线段的累计增量之和 */ long long query(int s, int t) { long long sum; int num_s, num_t; /* 被公共增量影响的元素个数 */ sum = num_s = num_t = 0; for (s = s+M-1, t = t+M+1; (s^t) != 1;) { //加上匹配线段的和 if (~s&1) { sum += tree[s^1].sum; num_s += (tree[s^1].t-tree[s^1].s+1); } if (t&1) { sum += tree[t^1].sum; num_t += (tree[t^1].t-tree[t^1].s+1); } //加上累计增量 s >>= 1; t >>= 1; sum += num_s*tree[s].d; sum += num_t*tree[t].d; } //至此,s和t拥有共同的父亲,从它们的父亲开始往上继续加上累计增量,直至根节点 for (s >>= 1;s > 0; s >>= 1) { sum += (num_s+num_t)*tree[s].d; } return sum; } /* 除了改变匹配线段的公共增量外,还要改变所有覆盖了匹配线段的线段的sum值 */ void add(int s, int t, int d) { int num_s, num_t; num_s = num_t = 0; for (s = s+M-1, t = t+M+1; (s^t) != 1;) { //更新匹配线段的公共增量和sum if (~s&1) { tree[s^1].d += d; tree[s^1].sum += (tree[s^1].t-tree[s^1].s+1)*d; num_s += (tree[s^1].t-tree[s^1].s+1); } if (t&1) { tree[t^1].d += d; tree[t^1].sum += (tree[t^1].t-tree[t^1].s+1)*d; num_t += (tree[t^1].t-tree[t^1].s+1); } s >>= 1; t >>= 1; tree[s].sum += num_s*d; tree[t].sum += num_t*d; } for (s >>= 1; s > 0; s >>= 1) { tree[s].sum += (num_s+num_t)*d; } } int main() { int n, q, i, s, t, value; long long sum; char action; scanf("%d %d", &n, &q); for (i = 1; i <= n; i++) { scanf("%d", A+i); } /* M表示叶子节点的个数,为了建造完全二叉树,M必须是2的n次幂 * 树的第一个和最后一个叶子节点不能使用,所以叶子 * 节点至少要有n+2个,综上所述,M是大于等于(n+2)且可以 * 表示为2的n次幂的最小整数 */ for (M = 1; M < (n+2); M <<= 1); make_tree(0, M-1); while (q--) { getchar(); scanf("%c %d %d", &action, &s, &t); if (action == 'Q') { sum = query(s, t); printf("%lld\n", sum); } else { scanf("%d", &value); if (value != 0) { add(s, t, value); } } } return 0; }