题目大意:
有一串数an,允许对任一个区间做加法还有乘法,问某个区间的求和。
例如:
1 2 3 4 5
进行操作[1,3]加1
数列变为:
2 3 4 4 5
进行操作[1,5]乘2
数列变为:
4 6 8 8 10
现在问[1,5]求和。
输出:36
解题思路:
数据量n=1e5 , 操作数m=1e5,暴力是万万不得。不用懒惰传播复杂度为O(n^2),妥妥tle
那我们很容易想到懒惰传播。问题是:这里的懒惰标签怎么打,我们知道假如只有乘法或者加法,那么这个懒惰传播还是好写的。对于只有加法的懒惰传播,每个节点的更新如下:
其中 st表示线段树,lazy[root]表示我们打的懒惰标签,l和r分别为当前线段树遍历的区间。假如只有乘标签,那么我们每个节点的更新如下:
但是,现在既有加法还有乘法,我们怎么更新一个节点呢?首先,我们知道肯定要做两个懒惰标签,其实我们在草稿纸模拟一下情况,标签有交替更新的情况,这是最难处理的。其中最难搞的是先遇到加法然后遇到乘法(大家可以排列组合一下++ ,+*,*+, **这四种更新标签的方法),我们怎么告诉节点应该是怎么更新呢?由乘法分配率: (a+b)*x=a*x+b*x,那么我们这里是不是应该可以在更新乘法的时候,让之前的加法标签更新一下呢。这个是本题最难想到的。我们每个节点的更新如下:
其中 ,分别代表乘法和加法的懒标签。
同时我们可以写出在向下传递标签时候怎么更新(注意,标签怎么更新是和节点怎么更新有关!所以我们必须准确找到节点怎么更新,而怎么让节点更新也是懒惰传播最难的地方)
modn是取模。left是往左走,right是往右走。
完整代码:
#include <bits/stdc++.h>
#define int long long
#define left(x) x<<1
#define right(x) (x<<1)+1
using namespace std;
int MODN;
const int MAXN=1e5+10;
int st[MAXN*4];
int lazyadd[MAXN*8];
int lazymul[MAXN*8];
int A[MAXN];
int modn(int a,int b){
return ((a % b) + b) % b;
}
int build(int root,int l,int r){
if(l==r)return st[root]=A[l];
int m=l+(r-l)/2;
int lv=build(left(root),l,m);
int rv=build(right(root),m+1,r);
return st[root]=lv+rv;
}
int upd1(int root,int l,int r,int ql,int qr,int val){
if(lazyadd[root]!=0 || lazymul[root]!=1){
st[root]=modn((lazymul[root]*st[root]+lazyadd[root]*(r-l+1)),MODN);
lazyadd[left(root)]=modn(((lazyadd[left(root)]*lazymul[root])+lazyadd[root]),MODN);
lazyadd[right(root)]=modn(((lazyadd[right(root)]*lazymul[root])+lazyadd[root]),MODN);
lazymul[left(root)]=modn((lazymul[root]*lazymul[left(root)]),MODN);
lazymul[right(root)]=modn((lazymul[root]*lazymul[right(root)]),MODN);
lazyadd[root]=0;
lazymul[root]=1;
}
if(l>=ql &&r<=qr){
lazymul[left(root)]=modn((lazymul[left(root)]*val),MODN);
lazyadd[left(root)]=modn((lazyadd[left(root)]*val),MODN);
lazymul[right(root)]=modn((lazymul[right(root)]*val),MODN);
lazyadd[right(root)]=modn((lazyadd[right(root)]*val),MODN);
return st[root]=modn((st[root]*val),MODN);
}
if(l>qr||r<ql)return st[root];
int m=l+(r-l)/2;
int lf=upd1(left(root),l,m,ql,qr,val);
int rg=upd1(right(root),m+1,r,ql,qr,val);
return st[root]=modn((lf+rg),MODN);
}
int upd2(int root,int l,int r,int ql,int qr,int val){
if(lazyadd[root]!=0 || lazymul[root]!=1){
st[root]=modn((lazymul[root]*st[root]+lazyadd[root]*(r-l+1)),MODN);
lazyadd[left(root)]=modn(((lazyadd[left(root)]*lazymul[root])+lazyadd[root]),MODN);
lazyadd[right(root)]=modn(((lazyadd[right(root)]*lazymul[root])+lazyadd[root]),MODN);
lazymul[left(root)]=modn((lazymul[root]*lazymul[left(root)]),MODN);
lazymul[right(root)]=modn((lazymul[root]*lazymul[right(root)]),MODN);
lazyadd[root]=0;
lazymul[root]=1;
}
if(l>=ql && r<=qr){
lazyadd[left(root)]=modn((lazyadd[left(root)]+val),MODN);
lazyadd[right(root)]=modn((lazyadd[right(root)]+val),MODN);
return st[root]=modn((st[root]+val*(r-l+1)),MODN);
}
if(l>qr || r<ql)return st[root];
int m=l+(r-l)/2;
int lv=upd2(left(root),l,m,ql,qr,val);
int rv=upd2(right(root),m+1,r,ql,qr,val);
return st[root]=modn((lv+rv),MODN);
}
int query(int root,int l,int r,int ql,int qr){
if(lazyadd[root]!=0 || lazymul[root]!=1){
st[root]=modn((lazymul[root]*st[root]+lazyadd[root]*(r-l+1)),MODN);
lazyadd[left(root)]=modn(((lazyadd[left(root)]*lazymul[root])+lazyadd[root]),MODN);
lazyadd[right(root)]=modn(((lazyadd[right(root)]*lazymul[root])+lazyadd[root]),MODN);
lazymul[left(root)]=modn((lazymul[root]*lazymul[left(root)]),MODN);
lazymul[right(root)]=modn((lazymul[root]*lazymul[right(root)]),MODN);
lazyadd[root]=0;
lazymul[root]=1;
}
if(l>=ql && r<=qr)return st[root];
if(l>qr || r<ql)return 0;
int m=l+(r-l)/2;
int lv=query(left(root),l,m,ql,qr);
int rv=query(right(root),m+1,r,ql,qr);
return modn((lv+rv),MODN);
}
int32_t main(){
int n,m;
scanf("%lld%lld%lld",&n,&m,&MODN);
for(int i=0;i<n;i++)
{scanf("%lld",&A[i]);
}
build(1,0,n-1);
memset(lazyadd,0,sizeof(lazyadd));
for(int i=0;i<8*MAXN;i++)lazymul[i]=1;
for(int i=0;i<m;i++){
int c,x,y;
scanf("%lld%lld%lld",&c,&x,&y);
if(c==1){
int val;
scanf("%lld",&val);
upd1(1,0,n-1,x-1,y-1,val);
}
else if(c==2){
int val;
scanf("%lld",&val);
upd2(1,0,n-1,x-1,y-1,val);
}else{
int ans=query(1,0,n-1,x-1,y-1);
printf("%lld\n",ans);
}
}
return 0;
}