P3372 【模板】线段树 1
------------------------
线段是是一种非常重要的数据结构,尤其在暴力时候
-------------------------
线段树我觉得就是一个暴力的暴力的暴力数据结构,支持许多操作,比如说最大值,最小值,区间加,区间和等
------------------------
线段树的优化关键在于一个懒标记
------------------------
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 struct kk{ 6 int l,r;//每一个节点包含的左右区间 7 long long add,tql;//每一个节点的lazy和区间和 8 }t[10000005]; 9 long long n,m,a[10000005],p,x,y,pp; 10 void build(int x,int y,int z){ 11 t[x].l=y; t[x].r=z;//这个点的控制的左右区间 12 if(y==z){//一开始,只有叶节点赋值 13 t[x].tql = a[y]; 14 return ; 15 } 16 int midd =(y+z)>>1;//中间点,因为线段树就是从中间开始分裂的 17 build(2*x,y,midd); 18 build(2*x+1,midd+1,z); 19 t[x].tql=max(t[x*2].tql+t[x*2+1].tql,(long long)0);//递归计算 20 21 } 22 void spr(int ooo){ 23 if(t[ooo].add){ 24 t[2*ooo].tql+=t[ooo].add*(t[2*ooo].r-t[2*ooo].l+1);//把这个点的实际值变成左右之和 25 t[ooo*2+1].tql+=t[ooo].add*(t[ooo*2+1].r - t[ooo*2+1].l+1);//左右之和为加的值和区间节点数之和 26 t[ooo*2].add+=t[ooo].add; 27 t[ooo*2+1].add+=t[ooo].add;//分发标记 28 t[ooo].add =0; //分发完了,这没了 29 } 30 } 31 void pluss(int oo, int ll,int rr,int kk) 32 if(ll <= t[oo].l && rr>=t[oo].r){//包含就直接加 33 t[oo].tql+=(long long) kk*(t[oo].r - t[oo].l+1);//统计一下答案 34 t[oo].add +=kk;//记上标记 35 return ; 36 } 37 spr(oo); 38 int midd = (t[oo].l+t[oo].r)>>1; 39 if(x<=midd) pluss(oo*2,x,y,kk);//分发标记 40 if(y>midd) pluss(oo*2+1,x,y,kk); 41 t[oo].tql = max(t[oo<<1].tql+ t[oo*2+1].tql,(long long)0);//重新统计 42 } 43 long long pr(int p,int x,int y){//查询 44 if(x<=t[p].l&&y>=t[p].r) return t[p].tql;//包含直接返回 45 spr(p); 46 int midd = t[p].l+t[p].r>>1; 47 long long ans =0; 48 if(x<=midd) ans+=pr(p<<1,x,y);//分摊查询 49 if(y>midd) ans += pr(p*2+1,x,y); 50 return ans; 51 } 52 int main(){ 53 cin>>n>>m; 54 for(int i=1;i<=n;++i) cin>>a[i]; 55 build(1,1,n); 56 for(int i=1;i<=m;++i){ 57 scanf("%lld",&p); 58 if(p==1){ 59 scanf("%lld%lld%lld",&x,&y,&pp); 60 pluss(1,x,y,pp); 61 } 62 else { 63 scanf("%lld%lld",&x,&y); 64 cout<<pr(1,x,y)<<endl; 65 } 66 } 67 }