//线段树单点修改求区间和模板
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <stdio.h>
#include <vector>
#define maxn 100007
using namespace std;
int Sum[maxn<<2], Add[maxn<<2]; //Add为懒惰标记,线段树数组大概大小为最大值的4倍,防止超限;
int A[maxn],n; //数据数组
void Pushup(int rt) //下推更新节点信息
{
Sum[rt]=Sum[2*rt]+Sum[2*rt+1];
}
//点修改
void updata(int L,int C,int l,int r,int rt)
{
if(l==r)
{
Sum[rt]+=C;
return;
}
int m=(l+r)/2;
if(L<=m) updata(L,C,l,m,rt<<1);
else updata(L,C,m+1,r,(rt<<1)+1);
Pushup(rt);
}
void PushDown(int rt,int ln,int rn){
//ln,rn分别为左子树,右子树的数字数量
if(Add[rt])
{
Add[rt*2]+=Add[rt];
Add[rt*2+1]+=Add[rt];
//修改子节点的Sum值使之与对应的Add相对应;
Sum[rt*2]+=Add[rt]*ln;
Sum[rt*2+1]+=Add[rt]*rn;
//清除本节点标记
Add[rt]=0;
}
}
//区间修改;
void Updata(int L,int R,int C,int left,int right,int rt)
{ //L,R表示操作区间,l,r表示当前节点区间;
if(L<=left&&right<=R){ //如果本区间完全在操作区间内 ;
Sum[rt]+=C*(right-left+1); //更新数字和,向上保持正确输出 ;
Add[rt]+=C; //增加Add标记,表示本区间Sum正确,子区间的值任然需要Add的值来调整;
return ;
}
int m=(left+right)>>1;
PushDown(rt,m-left+1,right-m) ;//下推标记;
if(L<=m) Updata(L,R,C,left,m,rt<<1);
if(R > m) Updata(L,R,C,m+1,right,(rt<<1)+1);
Pushup(rt); //更新本节点的信息;
}
//下推标记的函数
//去检查询
int Query(int L,int R,int l,int r,int rt)
{
//L,R表示操作区间,l,r表示当前结点区间
if(L<=l&&r<=R)
{
return Sum[rt];
}
int m=(l+r)>>1;
PushDown(rt,m-l+1,r-m);
int ANS=0;
if(L<=m) ANS+=Query(L,R,l,m,rt<<1);
if(R>m) ANS+=Query(L,R,m+1,r,(rt<<1)+1);
return ANS;
}
int main()
{
memset(Sum,0,maxn<<2);
memset(A,0,maxn<<2);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>A[i];
updata(i,A[i],1,n,1);
}
cout<<Query(3,5,1,n,1);
return 0;
}
线段树基础模板(递归实现,单点修改,区间求和,区间求和)
最新推荐文章于 2020-10-10 19:46:31 发布