关于对线段树模板的简化:
在刚开始学线段树时,我们学习的是利用完全二叉树的性质来建立子节点:左儿子的节点等于父亲节点序号乘2,右儿子的节点的序号等于左儿子的节点数加1,并且选用结构体的方式来存储信息。大概就是这样一个思路。
由于考虑到结构体引用慢,并且在可持久化线段树(主席树)中,已经不满足完全二叉树的性质,为了统一线段树的写法,建议大家统一使用动态开节点建树的方法。变量名的改变变化不大,列举如下:
lson[p]表示p节点的左儿子编号(注意是编号!!!不是维护序列的区间!!)rson[p]同理。
sum[p]表示一段区间,其他信息可以自行维护。
ndnum表示节点个数。
root表示根节点。
那么细节就看代码吧。
#include<bits/stdc++.h>
#define maxn 400000
using namespace std;
long long root,a[maxn],n,m,lson[maxn],rson[maxn],ll[maxn],rr[maxn],ndnum=0,add[maxn],sum[maxn],L,R;
void down(long long t)
{
if(add[t])
{
sum[lson[t]]+=add[t]*(rr[lson[t]]-ll[lson[t]]+1);
sum[rson[t]]+=add[t]*(rr[rson[t]]-ll[rson[t]]+1);
add[lson[t]]+=add[t];
add[rson[t]]+=add[t];
add[t]=0;
}
}
void update(long long t)
{
sum[t]=sum[lson[t]]+sum[rson[t]];
return;
}
void build(long long &t,long long l,long long r)//t是带了地址符号的!!
{
t=++ndnum;//动态开节点
ll[t]=l;rr[t]=r;
if(l==r)
{
sum[t]=a[l];add[t]=0;
return;
}
long long mid=(l+r)/2;
build(lson[t],l,mid);
build(rson[t],mid+1,r);
update(t);
}
void modify(long long t,long long l,long long r,long long k)
{
if(L<=l&&r<=R)
{
sum[t]+=k*(r-l+1);
add[t]+=k;
return;
}
down(t);
long long mid=(l+r)/2;
if(L<=mid) modify(lson[t],l,mid,k);
if(R>mid) modify(rson[t],mid+1,r,k);
update(t);
}
long long query(long long t,long long l,long long r)
{
if(L<=l&&r<=R) return sum[t];
down(t);
long long mid=(l+r)/2;
long long ans=0;
if(L<=mid) ans+=query(lson[t],l,mid);
if(R>mid) ans+=query(rson[t],mid+1,r);
return ans;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(long long i=1;i<=n;++i)
scanf("%lld",&a[i]);
build(root,1,n);
for(int i=1;i<=m;++i)
{
long long op,k;
scanf("%lld",&op);
if(op==1)
{
scanf("%lld%lld%lld",&L,&R,&k);
modify(root,1,n,k);
}
else
{
scanf("%lld%lld",&L,&R);
printf("%lld\n",query(root,1,n));
}
}
return 0;
}