原题链接:A Simple Problem with Integers
题意:给定一个数列A[1],A[2]...A[N]以及Q个操作,按顺序行这些操作,操作分为两种:1、给出l,r,x 对A[l],A[l+1]...A[r]同时加上x
2、给出l,r 求A[l]+A[l+1]+...+A[r]的值
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
/*
用树状数组实现,复杂度是Q*lg(N)
对区间[l,r]同时加上x:令s(i)为加x之前的和,s'(i)为加x之后的和
1. i<l s'(i) = s(i)
2. l<=i<=r s'(i) = s(i) + x*(i-l+1) = s(i) + x*i-x*(l-1)
3. r<i s'(i) = s(i) + x*(r-l+1)
sum(bit, i)代表树状数组bit的前i项和
加上x之后的前i项和s'(i) = sum(bit1,i)*i + sum(bit0,i);
在间[l,r]同时加上x需要以下4步(此操作保证l<i<r时s'(i)的增量是(i-l+1)*x,同时不应想次区间前后的值):
1、在bit0的l位置上加上-x(l-1)
2、在bit0的r+1位置上加上rx
3、在bit2的l位置上加上x
4、在bit2的r+1位置上加上-x
若求[l,r]内的和,即是s'(r)-s'(l-1)
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX_N = 100001;
const int MAX_Q = 100001;
typedef long long ll;
//输入数据
int N, Q;
int A[MAX_N];
char T[MAX_Q];
int L[MAX_Q], R[MAX_Q], X[MAX_Q];
//BIT树状数组
ll bit0[MAX_N + 1], bit1[MAX_N + 1];
ll sum(ll *b, int i){ //sum(bit, i)是树状数组bit的前i项和
ll s = 0;
while(i > 0){
s += b[i];
i -= i & -i;
}
return s;
}
void add(ll *b, int i, int v){ //i位置上加v
while(i <= N){
b[i] += v;
i += i & -i;
}
}
void solve(){
for(int i = 1;i <= N;i ++){
add(bit0, i, A[i]);
}
for(int i = 1;i <= Q;i ++){
if(T[i] == 'C'){
add(bit0, L[i], -X[i] * (L[i] - 1));
add(bit1, L[i], X[i]);
add(bit0, R[i] + 1, X[i] * R[i]);
add(bit1, R[i] + 1, -X[i]);
}else{
ll res = 0;
res += sum(bit0, R[i]) + sum(bit1, R[i]) * R[i];
res -= sum(bit0, L[i] - 1) + sum(bit1, L[i] - 1) * (L[i] - 1);
printf("%lld\n", res);
}
}
}
int main(){
scanf("%d%d", &N, &Q);
for(int i = 1;i <= N;i ++)
scanf("%d", &A[i]);
for(int i = 1;i <= Q;i ++){
scanf(" %c", &T[i]);
if(T[i] == 'C')
scanf("%d%d%d", &L[i], &R[i], &X[i]);
else
scanf("%d%d", &L[i], &R[i]);
}
solve();
return 0;
}
//用线段树实现,复杂度Q*lgN
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAX_N = 100001;
const int MAX_Q = 100001;
const int DAT_SIZE = (1 << 18) - 1;
//输入数据
int N, Q;
int A[MAX_N];
char T[MAX_Q];
int L[MAX_Q], R[MAX_Q], X[MAX_Q];
//线段树
ll data[DAT_SIZE]; //data[i]:第i个节点对应的区间的所有元素共同加上的值
ll datb[DAT_SIZE]; //datb[i]:第i个节点对应的区间除去data[i]之外的其他值得和
//对区间[a,b)同时加x,k是节点的编号,对应的区间[l,r)
void add(int a, int b, int x, int k, int l, int r){
if(a <= l && b >= r){ //[a,b)包含[l,r)
data[k] += x;
}else if(l < b && r > a){ // [a,b)与[l,r)有交集(l<a<r<b || l<a<b<r || a<l<b<r)
datb[k] += (min(b, r) - max(a, l)) * x; //保存交集区间的和
add(a, b, x, k * 2 + 1, l, (l + r) / 2); //左孩子
add(a, b, x, k * 2 + 2, (l + r) / 2, r); //右孩子
}
}
//计算[a,b)的和,k是节点的编号,对应的区间是[l,r)
ll sum(int a, int b, int k, int l, int r){
if(b <= l || r <= a){ //没有交集
return 0;
}if(a <= l && r <= b){ //[a,b)包含[l,r)
return data[k] * (r - l) + datb[k]; //节点保存的和
}else{ //(b > l && r > a)
ll res = (min(b, r) - max(a, l)) * data[k];
int mid = (l + r) / 2;
res += sum(a, b, k * 2 + 1, l, mid);
res += sum(a, b, k * 2 + 2, mid, r);
return res;
}
}
void solve(){
for(int i = 0;i < N;i ++){ //起点从0开始
add(i, i + 1, A[i], 0, 0, N);
}
for(int i = 0;i < Q;i ++){
if(T[i] == 'C'){
add(L[i] - 1, R[i], X[i], 0, 0, N); //加
}else {
printf("%lld\n", sum(L[i] - 1, R[i], 0, 0, N)); //求和
}
}
}
int main(){
scanf("%d%d", &N, &Q);
for(int i = 0;i < N;i ++)
scanf("%d", &A[i]);
for(int i = 0;i < Q;i ++){
scanf(" %c", &T[i]);
if(T[i] == 'C')
scanf("%d%d%d", &L[i], &R[i], &X[i]);
else
scanf("%d%d", &L[i], &R[i]);
}
solve();
return 0;
}