poj3468
题意:一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和。(这里区间指的是数列中连续的若干个数)对每次询问给出结果。
分析:线保留型线段树,线段树中每个节点有两个变量:增量与和,一个记录当前节点对应区间被整体增加了几,另一个记录该区间的真子区间被增加了之后的和是多少。(该区间数字当前和=和+增量×区间长度)
修改时更新路线上不完整覆盖的节点的和变量以及恰好完整覆盖的节点的增量变量。询问可以通过回溯过程通过各节点两变量值求得。
三种线段树,参见:http://www.cnblogs.com/rainydays/archive/2011/09/06/2169082.html
#include <cstring> #include <cstdio> #include <cstdlib> #include <iostream> using namespace std; const int maxn = 100001; struct cnode { int l, r; long long nsum, inc; cnode *pleft, *pright; }; int n, q, ncount; cnode tree[maxn * 3]; void buildtree(cnode *proot, int s, int e) { proot->l = s; proot->r = e; proot->inc = 0; proot->nsum = 0; if (s != e) { ncount++; proot->pleft = tree + ncount; ncount++; proot->pright = tree + ncount; buildtree(proot->pleft, s, (s + e) / 2); buildtree(proot->pright, (s + e) / 2 + 1, e); } } void insert(cnode *proot, int s, int e, long long increase) { int mid; if (s > proot->r || e < proot->l) return; s = max(s, proot->l); e = min(e, proot->r); mid = (proot->l + proot->r) / 2; if (s == proot->l && e == proot->r) { proot->inc += increase; return; } proot->nsum += (e - s + 1) * increase; insert(proot->pleft, s, e, increase); insert(proot->pright, s, e, increase); } long long query(cnode *proot, int s, int e) { int mid; if (s > proot->r || e < proot->l) return 0; s = max(s, proot->l); e = min(e, proot->r); mid = (proot->l + proot->r) / 2; if (proot->l == s && proot->r == e) return proot->nsum + proot->inc * (proot->r - proot->l + 1); long long inc_value = proot->inc * (e - s + 1); return query(proot->pleft, s, e) + query(proot->pright, s, e) + inc_value; } int main() { int i, h, s, e; char ch; scanf("%d%d", &n, &q); ncount = 0; buildtree(tree, 1, n); for (i = 0; i < n; i++) { scanf("%d", &h); insert(tree, i + 1, i + 1, h); } for (i = 0; i < q; i++) { getchar(); scanf("%c", &ch); if (ch == 'C') { scanf("%d%d%d", &s, &e, &h); insert(tree, s, e, h); } else { scanf("%d%d", &s, &e); printf("%lld\n", query(tree, s, e)); } } return 0; }