#include <bits/stdc++.h>
#define endl '\n'
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=1000010;
struct node
{
ll sum,lazy;
int l,r;
}tr[N<<2];//线段树结点要开数据量的四倍
ll input[N];
int n,m;
void pushup(int u)//向上更新
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void change(int u,ll lazy)
{
tr[u].sum+=(tr[u].r-tr[u].l+1)*lazy;//利用父节点的懒标记更新当前结点的sum
tr[u].lazy+=lazy;//更新当前节点的懒标记
}
void pushdown(int u)//向下更新
{
if(tr[u].lazy)
{
change(u<<1,tr[u].lazy);//利用懒标记更新左右儿子
change(u<<1|1,tr[u].lazy);
tr[u].lazy=0;//已用懒标记更新左右二次,懒标记清零
}
}
void build(int u,int l,int r)//建立线段树
{
tr[u].l=l,tr[u].r=r;
if(l==r)//当左右端点相同说明为叶子节点
{
tr[u].sum=input[l];
return ;
}
int mid=(r+l)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void add(int u,int pos,ll k)//单点修改
{
if(tr[u].l==tr[u].r)//如果左右端点相同说明找到了要查询的点
{
tr[u].sum+=k;
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(mid>=pos)add(u<<1,pos,k);//单点修改只需要修改一个点所以用if else
else add(u<<1|1,pos,k);
pushup(u);//返回更新祖宗节点
}
ll query(int u,int l,int r)//区间询问
{
if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
pushdown(u);//询问时需要用懒标记实时更新所查询到的结点
ll res=0;
int mid=tr[u].l+tr[u].r>>1;//当前区间中点
if(l<=mid)res+=query(u<<1,l,r);//如果当前结点的中点在所要查询区间左端点的右边
//则说明左儿子包含一部分查询区间
if(r>mid)res+=query(u<<1|1,l,r);//右儿子结点同理
return res;
}
void modify(int u,int l,int r,ll k)//区间修改
{
if(tr[u].l>=l&&tr[u].r<=r)
{
change(u,k);
return ;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)modify(u<<1,l,r,k);
if(r>mid)modify(u<<1|1,l,r,k);
pushup(u);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>input[i];
}
build(1,1,n);
for(int i=0;i<m;i++)
{
int type;
cin>>type;
if(type==1)
{
int l,r;
ll k;
cin>>l>>r>>k;
modify(1,l,r,k);
}
else
{
int l,r;
cin>>l>>r;
cout<<query(1,l,r)<<endl;
}
}
return 0;
}
基本线段树模板
于 2023-01-31 19:08:48 首次发布