题目大意:线段树区间加减,区间求和。
时间限制:5000ms
进行了位运算和输入优化,用时1469ms
分析:
build函数是建树,边界时把数据输到add数组里,add数组也就是常说的lazy标记,再把add的值赋给sum,建树完毕。
用sum[o]表示“如果只执行结点o及其子孙结点中的add操作,结点o对应区间中的所有数之和”,这样可以方便维护。
在执行update操作时,递归访问到的结点全部都要维护,并且是在递归返回后维护,因为这样子树的sum已经更新。
查询操作多加一个参数,表示当前区间的所有祖先结点add值之和,用全局变量sumv表示答案。
接下来是一些位运算优化,自行看代码。
数组开2.5倍区间长度会爆,开3倍不会。
数据类型用long long.
#include <iostream> #include <cstdio> using namespace std; typedef long long LL; char c; int n, q, x, y, z; LL sumv, sum[300005], add[300005]; void build(int o, int L, int R) { if(L == R) { scanf("%lld", &add[o]); sum[o] = add[o]; return; } int m = (L+R) >> 1; build(o<<1, L, m); build(o<<1 | 1, m+1, R); sum[o] = sum[o<<1] + sum[o<<1 | 1]; } void maintain(int o, int L, int R) { sum[o] = 0; if(R > L) sum[o] = sum[o<<1] + sum[o<<1 | 1]; sum[o] += add[o] * (R-L+1); } void update(int o, int L, int R) { if(x <= L && y >= R) add[o] += z; else { int m = (L+R) >> 1; if(x <= m) update(o<<1, L, m); if(y > m) update(o<<1 | 1, m+1, R); } maintain(o, L, R); } void query(int o, int L, int R, LL addv) { if(x <= L && y >= R) sumv += sum[o] + addv * (R-L+1); else { int m = (L+R) >> 1; if(x <= m) query(o<<1, L, m, addv+add[o]); if(y > m) query(o<<1 | 1, m+1, R, addv+add[o]); } } int main() { scanf("%d%d", &n, &q); build(1, 1, n); while(q--) { cin>>c; if(c == 'Q') { scanf("%d%d", &x, &y); sumv = 0; query(1, 1, n, 0); printf("%lld\n", sumv); } else { scanf("%d%d%d", &x, &y, &z); update(1, 1, n); } } return 0; }