今天我们把线段树的另一个模板看一下:
在这里,我们注意到乘的操作,因此我们用两个懒标记来分别表示加和乘,这时我们面临了一个问题,就是当我们把标记往下传时,它的儿子怎么知道是先乘还是先加?
究其原因,其实是括号在作祟,因此,我们每一次乘的时候,出了lazy*=num,我们把加法的标记也乘一下,这样子我们先乘后加,就巧妙地化解了括号的问题。
同时,还有一点我们需要注意,那就是当标记下移时,子节点的加法标记出了加上父节点的加法标记,还要*父节点的乘法标记。
举个例子,考虑原来的儿子为*2+3,父亲的懒标记也是*2+3,当我们下放时,应该变成*4+9,而其中的9就是由3+3*2得到的。
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[10001],m,sum[4*10001];
int tree[4*10001];
int lazy1[4*10001];
int lazy2[4*10001];
void pushdown(int p,int l,int r);
int calc(int p,int l,int r,int x,int y,int k);
void build(int p,int l,int r){//建树 lazy2[p]=1;if(l==r){tree[p]=a[l]*a[l];sum[p]=a[l];return;}int mid=(l+r)/2;build(p*2,l,mid);build(p*2+1,mid+1,r);tree[p]=tree[p*2+1]+tree[p*2];sum[p]=sum[p*2+1]+sum[p*2];return;
}
void pushdown(int p,int l,int r){//lazy标记下移 int mid=(l+r)/2;lazy1[p*2]=(lazy2[p])*lazy1[p*2]+lazy1[p];lazy1[p*2+1]=lazy1[p*2+1]*(lazy2[p])+lazy1[p];lazy2[p*2]*=lazy2[p];lazy2[p*2+1]*=lazy2[p];tree[p*2]=(lazy2[p])*(lazy2[p])*tree[p*2]+2*lazy1[p]*(lazy2[p])*sum[2*p]+lazy1[p]*lazy1[p]*(mid-l+1);//更新子节点的值 tree[p*2+1]=(lazy2[p])*(lazy2[p])*tree[p*2+1]+2*lazy1[p]*(lazy2[p])*sum[2*p+1]+lazy1[p]*lazy1[p]*(r-mid);sum[p*2]*=(lazy2[p]);sum[p*2+1]*=(lazy2[p]);sum[p*2]+=lazy1[p]*(mid-l+1);sum[p*2+1]+=lazy1[p]*(r-mid);lazy1[p]=0;//自己因为下移清0 lazy2[p]=1;
}
void change1(int p,int l,int r,int x,int y,int num){if(x<=l&&r<=y){tree[p]+=2*num*(sum[p])+num*num*(r-l+1);sum[p]+=num*(r-l+1);lazy1[p]+=num;return;}if(lazy1[p]!=0||lazy2[p]!=1){//区间部分修改,需要下移 pushdown(p,l,r);}int mid=(l+r)/2;if(y<=mid) change1(p*2,l,mid,x,y,num);if(x>mid) change1(p*2+1,mid+1,r,x,y,num);if(x<=mid&&y>mid){change1(p*2,l,mid,x,mid,num);change1(p*2+1,mid+1,r,mid+1,y,num);}tree[p]=tree[2*p]+tree[2*p+1];sum[p]=sum[2*p]+sum[2*p+1];return;
}
void change2(int p,int l,int r,int x,int y,int num){if(x<=l&&r<=y){tree[p]=num*num*tree[p];sum[p]*=num;lazy2[p]*=num;lazy1[p]*=num;return;}if(lazy1[p]!=0||lazy2[p]!=1){//区间部分修改,需要下移 pushdown(p,l,r);}int mid=(l+r)/2;if(y<=mid) change2(p*2,l,mid,x,y,num);if(x>mid) change2(p*2+1,mid+1,r,x,y,num);if(x<=mid&&y>mid){change2(p*2,l,mid,x,mid,num);change2(p*2+1,mid+1,r,mid+1,y,num);}tree[p]=tree[2*p]+tree[2*p+1];sum[p]=sum[2*p]+sum[2*p+1];return;
}
int calc(int p,int l,int r,int x,int y,int k){if(l>=x&&r<=y){if(k==1) return tree[p];else return sum[p];}if(lazy1[p]!=0||lazy2[p]!=1){pushdown(p,l,r);}int mid=(l+r)/2;if(y<=mid) return calc(p*2,l,mid,x,y,k);if(x>mid) return calc(p*2+1,mid+1,r,x,y,k);return calc(p*2,l,mid,x,mid,k)+calc(p*2+1,mid+1,r,mid+1,y,k);
}
signed main(){cin>>n>>m;for(int i=1;i<=n;i++) scanf("%lld",&a[i]);build(1,1,n);for(int i=1;i<=m;i++){int x,y,k,op;scanf("%lld%lld%lld",&op,&x,&y);if(op==1){printf("%lld\n",calc(1,1,n,x,y,0));}else if(op==2) printf("%lld\n",calc(1,1,n,x,y,1));else if(op==3){scanf("%lld",&k);change2(1,1,n,x,y,k);}else{scanf("%lld",&k);change1(1,1,n,x,y,k);}}
}