萌新一枚,借鉴了许多大佬而总结下的笔记,可能有瑕疵或者错误,望指正。
1用于储存每次修改后所有状态的线段树
2不需要存储整颗树,每次修改只要新增单独的链即可
3区间修改不再用pushdown操作,而是选择在查询的过程中下放lazy标记
代码实现
一,树的存储
struct node{
int l,r,sum,lazy;
}tree[maxn<<5];
int tot;
int root[maxn];//记录每棵树的根节点
二,建立一棵以root[0]为根的树
int build(int l,int r,int p)
{
p=++tot;//添加新的节点
if(l==r)
{
tree[p].sum=a[l];
return p;
}
int mid=(l+r)>>1;
tree[p].l=build(l,mid,tree[p].l);//创建左节点
tree[p].r=build(mid+1,r,tree[p].r);
tree[p].sum=tree[tree[p].l].sum+tree[tree[p].r].sum;//向上合并求当前节点的和
return p;
}
三,单点修改
void update(int p,int q,int l,int r,int x,int v)//p为前一次修改的树,q为本次修改
{
if(l==r)//如果找到位置
{
tree[q].sum=tree[p].sum+v;
return;
}
tree[q].l=tree[p].l;//复制左右节点
tree[q].r=tree[p].r;
int mid=(l+r)>>1;
if(x<=mid)
{
tree[q].l=++tot;//如果要修改的点在左边,向左边分裂成一条新的链
update(tree[p].l,tree[q].l,l,mid,x,v);
}
else
{
tree[q].r=++tot;//右边同理
update(tree[p].r,tree[q].r,mid+1,r,x,v);
}
tree[q].sum=tree[tree[q].l].sum+tree[tree[q].r].sum;//合并求和
}
四,区间修改
void update(int p,int q,int l,int r,int ul,int ur,int v)
{
tree[q].l=tree[p].l;
tree[q].r=tree[p].r;
tree[q].lazy=tree[p].lazy;//复制原有节点的信息
if(ul<=l&&r<=ur)//如果当前区间在要修改的区间内,更改lazy标记
tree[q].lazy+=v;
else
{
int mid=(l+r)>>1;
if(l<=ur&&ul<=mid)//如果要修改的区间和左儿子有交集,那么就要新建一个左儿子
{
tree[q].l=++tot;
update(tree[p].l,tree[q].l,l,mid,ul,ur,v);
}
if(r>=ul&&ur>mid)//如果要修改的区间和右儿子有交集,那么就要新建一个右儿子
{
tree[q].r=++tot;
update(tree[p].r,tree[q].r,mid+1,r,ul,ur,v);
}
}
tree[q].sum=tree[p].sum+(min(ur,r)-max(l,ul)+1)*v;//向上求和,当前节点的值为原先的值加上区间修改的总的值
//如果ur,ul在区间[l.r]内,那么sum加上(ur-ul+1)*v;
//如果ur,ul和区间[l,r]有交集,sum加上相交部分乘以v
}
五,区间查询
int query(int l,int r,int ql,int qr,int lazy,int p)
{
if(ql<=l&&r<=qr)//查询区间在当前区间内,返回当前节点的值加上lazy所贡献的值
return tree[p].sum+lazy*(r-l+1);
int ans=0;
int mid=(l+r)>>1;
if(ql<=mid)//和左节点有交集
ans+=query(l,mid,ql,qr,lazy+tree[p].lazy,tree[p].l);//下放lazy标记
if(qr>mid)//和右节点有交集
ans+=query(mid+1,r,ql,qr,lazy+tree[p].lazy,tree[p].r);
return ans;
}
六,主函数应用
int main()
{
int n,q;
cin>>n>>q;//n个数据,q次操作
for(int i=1;i<=n;i++)
cin>>a[i];
root[0]=build(1,n,1);//新建一个以root[0]为根节点的线段树
int s=0;//作为第s次修改后根节点的编号
while(q--)
{
int op;
cin>>op;//输入操作
if(op==1)//查询操作
{
int l,r,k;//查询第k次修改后[l,r]的和
cin>>l>>r>>k;
cout<<query(1,n,l,r,0,root[k])<<'\n';
}
else if(op==2)//单点修改
{
int x,v;
cin>>x>>v;
root[++s]=++tot;
update(root[s-1],root[s],1,n,x,v);
}
else if(op==3)//区间修改
{
int l,r,v;
cin>>l>>r>>v;
root[++s]=++tot;
update(root[s-1],root[s],1,n,l,r,v);
}
}
return 0;
}