做了N遍,总算过了,太菜了写一下巩固一下
首先根据题目n的最大为100000,那么根据,,反正定义数组为maxn * 4绝对不会错。
因为数据中“保证任意时刻数列中所有元素的绝对值之和 <= 1e18”所以可以用long long定义。 f 为线段树数组,z 为标记
int n, m, a[100010];
// 线段树 懒标记
ll f[400040], z[400040];
首先要明白树的下标怎么算的, 树的根节点一般为 1 格式: 1 2 3 4 5 6 7 .... 因为我们可以发现,从1开始 i2和i2+1为其子节点 而对于任意一个节点 q 其父节点为 q/2(或q>>1)
inline void buildTree(int k,int l,int r){
if(l==r){ //当走到根节点时更新 f 值,并返回不在向下走。
f[k] = a[l];
return;
}
//取 l r 中点
int m = (l+r) / 2;
//递归调用
buildTree(k + k, l, m);
buildTree(k + k + 1, m + 1, r);
//回溯时从下到上计算f的值
f[k] = f[k + k] + f[k + k + 1];
}
将所需加的区间的 z 进行添加,并在访问时,将访问上有值的 z数组 的值更新的节点的两个子节点中,最后在回溯时计算节点值 节点值:f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m)); ::需要注意添加的 k 的值可能为报 int ,需要进行修改
//区间加
inline void addTree(int k, int l, int r, int x, int y, ll v){
if(l==x && r==y){
//当访问至当前区间时,打上懒标记
z[k] += v;
return;
}
//如果存在懒标记,且未到所需查询区间的最底层,更新懒标记下放
if(z[k])
z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
int m = (l+r) >> 1;
//需要注意区间的划分,因为左边时l到m,所以 y<=m 的时候才走左边;右边的区间为 m+1 到 r,即x>m(或x>=m+1)
if(y <= m) //所需添加的区间在以m为中点的左边
addTree(k+k, l, m, x, y, v);
else if(x > m) //在右边
addTree(k+k+1, m+1, r, x, y, v);
else //都有
addTree(k+k, l, m, x, m, v), addTree(k+k+1, m+1, r, m+1, y, v);
//回溯时更新线段树
f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
}
求和总体思路与添加一致,不过退出时不需要修改 z 的值,只需要在访问区间时进行修改非0 的 z。并且是有返回值的 dfs ,返回值存储在 ans 在回溯后返回。
inline ll getTree(int k, int l, int r,int x,int y){
if(l==x && r==y)
return f[k] + (z[k] * (r-l+1));
//更新懒标记
if(z[k])
z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
int m = (l+r) >> 1;
ll ans = 0;
if(y <= m) //在左边
ans += getTree(k+k, l, m, x, y);
else if(x > m) //在右边
ans += getTree(k+k+1, m+1, r, x, y);
else
ans += getTree(k+k, l, m, x, m) + getTree(k+k+1, m+1, r, m+1, y);
//回溯时更新线段树
f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
return ans;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m, a[100010];
// 线段树 懒标记
ll f[400040], z[400040];
//建树
inline void buildTree(int k,int l,int r){
if(l==r){
f[k] = a[l];
return;
}
int m = (l+r) / 2;
buildTree(k + k, l, m);
buildTree(k + k + 1, m + 1, r);
f[k] = f[k + k] + f[k + k + 1];
}
//区间加
inline void addTree(int k, int l, int r, int x, int y, ll v){
if(l==x && r==y){
//打上懒标记
z[k] += v;
return;
}
//更新懒标记下放
if(z[k])
z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
int m = (l+r) >> 1;
if(y <= m) //在左边
addTree(k+k, l, m, x, y, v);
else if(x > m) //在右边
addTree(k+k+1, m+1, r, x, y, v);
else //都有
addTree(k+k, l, m, x, m, v), addTree(k+k+1, m+1, r, m+1, y, v);
//回溯时更新线段树
f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
}
//区间求和
inline ll getTree(int k, int l, int r,int x,int y){
if(l==x && r==y)
return f[k] + (z[k] * (r-l+1));
//更新懒标记
if(z[k])
z[k+k] += z[k], z[k+k+1] += z[k], z[k] = 0;
int m = (l+r) >> 1;
ll ans = 0;
if(y <= m) //在左边
ans += getTree(k+k, l, m, x, y);
else if(x > m) //在右边
ans += getTree(k+k+1, m+1, r, x, y);
else
ans += getTree(k+k, l, m, x, m) + getTree(k+k+1, m+1, r, m+1, y);
//回溯时更新线段树
f[k] = f[k+k] + (z[k+k] * (m-l+1)) + f[k+k+1] + (z[k+k+1] * (r-m));
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
//建树
buildTree(1,1,n);
for( ;m--; ){
int temp,x,y;
scanf("%d%d%d",&temp,&x,&y);
if(temp&1){ //区间加
ll k;
scanf("%lld",&k);
addTree(1, 1, n, x, y, k);
}else{ //输出和
printf("%lld\n",getTree(1,1,n,x,y));
}
}
return 0;
}