POJ3468
题目链接
题目概述
给出一个包含有(N)个元素的数组(a),然后是(m)次操作,操作有以下两种类型:
Q x y
((x leq y))计算(sum_{i = x}^y a[i])C x y d
((x leq y))将区间([x,y])内部的每一个(a[i])加上(d).
对于第一种查询操作输出对应的结果,数据规模:
[1leq N,Mleq 10^5, -10^9 leq A_i leq 10^9, -10^4leq dleq 10^4.
]
题目分析
经典的树状数组区间修改和区间查询的题目,利用初始的前缀和数组和用两个树状数组维持区间修改后的(sum_{i=1}^na[i]).
利用差分转换的原理可以看看这个差分--OIwiki
通过:
[sum_{i=1}^{r+1}(r+1-i+1)cdot f_i - sum_{i=1}^{l}(l-i+1)cdot f_i
]
代码
/*
* @Author: Shuo Yang
* @Date: 2020-08-04 15:46:50
* @LastEditors: Shuo Yang
* @LastEditTime: 2020-08-04 16:58:19
* @FilePath: /Code/POJ/3468.cpp
*/
#include<iostream>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
ll t1[N];
ll t2[N];
ll a[N];
int n;
inline int lowbit(int x){
return x & -x;
}
void add(ll* t, int k, int x){
while( k <= n){
t[k] += x;
k += lowbit(k);
}
}
ll sum(ll* t, int k){
ll ans = 0;
while( k > 0){
ans += t[k];
k -= lowbit(k);
}
return ans;
}
void addL(int lef, int rig, int x){
add(t1, lef, x);
add(t2, lef, lef*x);
add(t1, rig+1, -x);
add(t2, rig+1, -(rig+1)*x);
}
int main(int argc, const char** argv) {
int m;
scanf("%d %d",&n,&m);
for(int i = 1; i <=n ; ++i){
scanf("%lld", &a[i]);
a[i] += a[i-1];
}
for(int i = 0; i <m; ++i){
char ch;
int x,y,c;
while( ch != 'Q' && ch !='C')
scanf("%c",&ch);
// printf("ch=%c**
", ch);
if(ch == 'Q'){
scanf("%d %d", &x,&y);
ll ans = (a[y]+(sum(t1,y)*(y+1) - sum(t2,y))) - (a[x-1]+(sum(t1,x)*x-sum(t2,x)));
printf("%lld
", ans);
}else if( ch == 'C'){
scanf("%d %d %d",&x,&y,&c);
addL(x,y,c);
}
}
return 0;
}
这里面的树状数组t1
是维护(sum_{i=1}^{n}(n+1)cdot a[i]),t2
数组是维护(sum_{i=1}^{n}icdot a[i]).
区间更新操作是对这两个操作进行的,至于区间求和在原来前缀和的基础上加上这个区间修改后的差分求和得到的就是最终的结果.
(纯粹的板子,但是我说不清楚,┭┮﹏┭┮2333333).