题目大意:线段树区间加减,区间求和。
时间限制: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;
}