题目链接:P3372 【模板】线段树 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
视频链接:这可能是全b站最通俗易懂的线段树入门教学视频了 P2 带标记的线段树_哔哩哔哩_bilibili
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, m, a[100001], f[400001], v[400001];
void buildtree(int k, int l, int r){ //建树,k为当前点的标号,l区间左端点,r区间右端点
if(l == r){ //走到底
f[k] = a[l];
return;
}
int m = (l + r) >> 1;
buildtree(k + k, l, m); //建左子树
buildtree(k + k + 1, m + 1, r); //建右子树
f[k] = f[k + k] + f[k + k + 1]; //该点的值为左子树和右子树的和
}
void insert(int k, int l, int r, int x, int y, int z){ //k为当前点的标号,l区间左端点,r区间右端点,在x-y这个区间加上z
if(l == x && r == y){ //区间重合
v[k] += z;
return;
}
f[k] += (y - x + 1) * z; //区间长度乘上z
int m = (l + r) >> 1;
if(y <= m) //s-t完全坐落在左区间
insert(k + k, l, m, x, y, z);
else{
if(x > m) //完全坐落在右区间
insert(k + k + 1, m + 1, r, x, y, z);
else{ //横跨两边
insert(k + k, l, m, x, m, z);
insert(k + k + 1, m + 1, r, m + 1, y, z);
}
}
}
int calc(int k, int l, int r, int x, int y, int p){ //k为下标,求区间l-r中x-y的和, p为根到当前结点v的和(不包括当前的点)
p += v[k];
if(l == x && r == y) //区间重合
return p * (r - l + 1) + f[k];
int m = (l + r) >> 1;
if(y <= m) //s-t完全坐落在左区间
return calc(k + k, l, m, x, y, p);
else{
if(x > m) //完全坐落在右区间
return calc(k + k + 1, m + 1, r, x, y, p);
else //横跨两边
return calc(k + k, l, m, x, m, p) + calc(k + k + 1, m + 1, r, m + 1, y, p);
}
}
signed main(){
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
buildtree(1, 1, n);
for(int i = 1; i <= m; i++){
int t; scanf("%lld", &t);
if(t == 1){
int x, y, k;
scanf("%lld%lld%lld", &x, &y, &k);
insert(1, 1, n, x, y, k);
}
else{
int x, y;
scanf("%lld%lld", &x, &y);
printf("%lld\n", calc(1, 1, n, x, y, 0));
}
}
}