A Simple Problem with Integers POJ - 3468
Psy给出了一个序列,Drh需要处理如下两种询问。
"C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。
“Q a b” 询问[a, b]区间中所有值的和。
Input
第一行包含两个整数N, Q。1 ≤ N,Q ≤ 100000.
第二行包含n个整数,表示初始的序列A (-1000000000 ≤ Ai ≤ 1000000000)。
接下来Q行询问,格式如题目描述。
Output
对于每一个Q开头的询问,你需要输出相应的答案,每个答案一行。
Examples
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
Hint
概述
之前有提到的基于RMQ和sum的线段树都是单点更新, 也就是从根节点开始走, 一直递归到对应的叶子节点
但对于一类要求区间更新的问题, 如果仍然以单点更新的逻辑去循环递归, 就会使得复杂度骤增, 极端情况可以达到NlogN.
lazy-tag思想算法
lazy-tag思想: 记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。
例如, 我们现在对[a,b]区间进行+c操作, 依然从根节点[1,n]开始递归执行update
如果刚好执行到一个子节点[l, r]使得x<=l&&y>=r, 也就是该区间完全包含在[x, y]种时, 我们依照update一样更新其值
重点在于, 此时我们不再更新其子节点的值, 就此return, 直至下次使用到的时候再去更新, 可以避免很多重复的操作.
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1 << 30;
const LL maxn = 1e5 + 10;
int N, Q, a[maxn];
LL sum[maxn*4], add[maxn*4];
void pushup(int id) { //向上更新
sum[id] = sum[id << 1] + sum[id << 1 | 1];
}
void pushdown(int id, int m){
if(add[id]){
add[id<<1] += add[id];
add[id<<1|1] += add[id]; //更新左右儿子的add
sum[id<<1] += (m-(m>>1))*add[id];//更新sum[id]
sum[id<<1|1] += (m>>1)*add[id];
add[id] = 0;
}
}
void build(int id, int l, int r) {
add[id] = 0;
if(l == r) {
sum[id] = a[l];
return;
}
int mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
pushup(id);
}
void update(int id, int l, int r, int x, int y, int c) {
if(x<=l && y>=r){
add[id] += c;
sum[id] += (LL)c*(r-l+1); //r-l+1为原中点
return; //lazy操作
}
pushdown(id, r-l+1); //使用到时再向下更新
int mid = (l+r)>>1;
if(x <= mid)
update(id << 1, l, mid, x, y, c);
if(y > mid)
update(id << 1 | 1, mid + 1, r, x, y, c);
pushup(id);
}
LL query(int id, int l, int r, int x, int y) {
if(x <= l && y >= r)
return sum[id];
pushdown(id, r-l+1); //使用到时再向下更新
int mid = (l + r) >> 1;
LL ret = 0;
if(x <= mid)
ret += query(id << 1, l, mid, x, y);
if(y > mid)
ret += query(id << 1 | 1, mid + 1, r, x, y);
return ret;
}
int main() {
scanf("%d%d", &N, &Q);
for(int i = 1; i <= N; i++)
scanf("%d", &a[i]);
ms(sum, 0);
build(1, 1, N);
char opt;
int a, b, c;
while(Q--) {
scanf(" %c", &opt);
if(opt == 'Q') {
scanf("%d%d", &a, &b);
printf("%lld\n", query(1, 1, N, a, b));
} else if(opt == 'C') {
scanf("%d%d%d", &a, &b, &c);
update(1, 1, N, a, b, c);
}
}
return 0;
}