题目链接
这是LibreOJ上的树状数组模板题,这题考查的是区间修改和区间查询。需要运用到差分的思想,和一个很重要的推导公式,公式如下:
在此附上一版AC代码:
#include<iostream>
using namespace std;
typedef long long ll;
ll a[1000000+10], b[1000000+10], bb[1000000+10], bshu[1000000+10], bbshu[1000000+10];
//b是a的差分数组,bb是i*b数组,bshu是维护b的树状数组,bbshu是维护bb数组的树状数组。
ll n, q;
ll lowbit(ll x){
return x & (-x);
}
void creat(){
for(ll i=1;i<=n;i++){
bshu[i] += b[i];
ll j=i+lowbit(i);
if(j<=n) bshu[j]+=bshu[i];
}
for(ll i=1;i<=n;i++){
bbshu[i] += bb[i];
ll j=i+lowbit(i);
if(j<=n) bbshu[j]+=bbshu[i];
}
}
//建树。
void add(ll k, ll v) {
ll v1 = k * v;
while (k <= n) {
bshu[k] += v, bbshu[k] += v1;
k += lowbit(k);
}
}
//单点加。
void add1(ll l, ll r, ll v) {
add(l, v), add(r + 1, -v); // 将区间加差分为两个前缀加
}
//区间加。
ll getsum(ll *t, ll k) {
ll ret = 0;
while (k) {
ret += t[k];
k -= lowbit(k);
}
return ret;
}
//前缀和。
ll getsum1(ll l, ll r) {
return (r + 1) * getsum(bshu, r) - 1 * l * getsum(bshu, l - 1) -
(getsum(bbshu, r) - getsum(bbshu, l - 1));
}
//区间查询。
int main()
{
scanf("%lld%lld", &n, &q);
for(int i=1;i<=n;i++){
scanf("%lld", &a[i]);
if(i==1) b[1]=a[1], bb[1]=b[1];
else{
b[i] = a[i]-a[i-1];
bb[i] = i*b[i];
}
}
creat();
int flag;
ll l, r, x;
for(int i=1;i<=q;i++){
scanf("%d", &flag);
if(flag==1){
scanf("%lld%lld%lld", &l, &r, &x);
add1(l, r, x);
}else if(flag==2){
scanf("%lld%lld", &l, &r);
printf("%lld\n", getsum1(l, r));
}
}
return 0;
}