|
/**//*
很早以前着手的题,采用了和Pku 2528 的做法做,可惜一直超时,原因是这题如果覆盖次数一多, 可能最后真正被完全覆盖的区间可能就没了,是的查询的时候全部结点都需要遍历,使得 原来O(log(n))可以解决的问题退化为O(n),金工实习和匡剑兄一起,于是决定请教一下他,经过匡剑兄的指点, 终于明白了,一共维护两个域,一个是当前结点的增量值,另外一个是以该结点为根的总和,(注: 祖先的分数不算在内) 那么一共两个操作:插入和询问可以这样: 插入:到达尾部结点时将增量加在该结点上,并且把该区间总增量返还给它父亲,一直到根 询问:一路走下去,并且将父亲的增量按权值分配给它的儿子,最后回溯。如此一来,插入和询问都是O(log(n)) */ #include <iostream>
using namespace std;
__int64 tree[2000010]; __int64 inc[2000010]; bool flag[2000010];
__int64 a[100010]; int n, m; int i;
__int64 Build(int p, int l, int r) { if(l == r) { tree[p] = a[l]; return tree[p]; } int mid = (l + r) / 2; tree[2*p] = Build(2*p, l, mid); tree[2*p+1] = Build(2*p+1, mid+1, r); return tree[2*p] + tree[2*p+1]; }
__int64 Query(int p, int s, int e, int l, int r) { int mid = (l + r) / 2; __int64 temp = 0;
if(s == l && e == r) { return tree[p]; }
temp = (__int64)(e-s+1)*inc[p];
if(e <= mid) { return Query(2*p, s, e, l, mid) + temp; }else if(mid + 1 <= s) { return Query(2*p+1, s, e, mid+1, r) + temp; }else { return Query(2*p, s, mid, l, mid) + Query(2*p+1, mid+1, e, mid+1, r) + temp; } }
__int64 Insert(int p, int s, int e, int l, int r, __int64 value) { if(s == l && e == r) { inc[p] += value; tree[p] += value * (r - l + 1); return value * (r - l + 1); }
int mid = (l + r) / 2; __int64 buf = 0;
if(e <= mid) { buf += Insert(2*p, s, e, l, mid, value); }else if(mid + 1 <= s) { buf += Insert(2*p+1, s, e, mid+1, r, value); }else { buf += Insert(2*p, s, mid, l, mid, value); buf += Insert(2*p+1, mid+1, e, mid+1, r, value); } tree[p] += buf; return buf; }
int main() {
int i, x, y; __int64 z; char str[5]; while(scanf("%d %d", &n, &m) != EOF) { for(i = 1; i <= n; i++) { scanf("%I64d", &a[i]); } tree[1] = Build(1, 1, n); memset(inc, 0, sizeof(inc)); while(m --) { scanf("%s", str); if(str[0] == 'Q') { scanf("%d %d", &x, &y); printf("%I64d\n", Query(1, x, y, 1, n) ); }else { scanf("%d %d %I64d", &x, &y, &z); tree[1] = Insert(1, x, y, 1, n, z); } } } return 0; }
|