题目链接:https://www.luogu.org/problemnew/show/P3373
题意:相比与模板1,这题多了一个操作,即将一段区间的数都乘k。因为乘法的优先级高,这里要采用乘法优先。实现上和模板1的差别在于:
0. 使用两个懒惰值,add表示要加的数,mul表示要乘的数,分别初始化为0,1。
1. 更新乘法时,要将原来的add也乘上mul。
2. 向下传递懒惰值时,先乘再加,对孩子的add属性,要先乘父亲的mul,在加父亲的add,孩子的mul直接乘父亲的mul即可。然后将父亲的add置0,mul置1。
3. 因为乘法和加法对取模运算都是自由的,计算过程可以直接取模。
AC代码:
#include<cstdio> #include<cctype> using namespace std; typedef long long LL; const int maxn=100005; inline int read(){ int x=0,f=0;char c=0; while(!isdigit(c)){f|=c=='-';c=getchar();} while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); return f?-x:x; } inline LL readLL(){ LL x=0;int f=0;char c=0; while(!isdigit(c)){f|=c=='-';c=getchar();} while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); return f?-x:x; } struct node{ int l,r; LL value,mul,add; }tr[4*maxn]; LL a[maxn],n,m,P,ans; void build(int v,int l,int r){ tr[v].l=l,tr[v].r=r,tr[v].add=0,tr[v].mul=1; if(l==r){ tr[v].value=a[r]; return; } int mid=(l+r)>>1; build(2*v,l,mid); build(2*v+1,mid+1,r); tr[v].value=(tr[2*v].value+tr[2*v+1].value)%P; } void spread(int v){ tr[2*v].value=tr[2*v].value*tr[v].mul%P; tr[2*v].value=(tr[2*v].value+(tr[2*v].r-tr[2*v].l+1)*tr[v].add)%P; tr[2*v+1].value=tr[2*v+1].value*tr[v].mul%P; tr[2*v+1].value=(tr[2*v+1].value+(tr[2*v+1].r-tr[2*v+1].l+1)*tr[v].add)%P; tr[2*v].add=tr[2*v].add*tr[v].mul%P; tr[2*v].add=(tr[2*v].add+tr[v].add)%P; tr[2*v].mul=tr[2*v].mul*tr[v].mul%P; tr[2*v+1].add=tr[2*v+1].add*tr[v].mul%P; tr[2*v+1].add=(tr[2*v+1].add+tr[v].add)%P; tr[2*v+1].mul=tr[2*v+1].mul*tr[v].mul%P; tr[v].mul=1,tr[v].add=0; } void update(int v,int l,int r,LL mu,LL ad){ if(tr[v].l==l&&tr[v].r==r){ if(ad){ tr[v].value=(tr[v].value+(tr[v].r-tr[v].l+1)*ad)%P; tr[v].add=(tr[v].add+ad)%P; } else{ tr[v].value=(tr[v].value*mu)%P; tr[v].add=(tr[v].add*mu)%P; tr[v].mul=(tr[v].mul*mu)%P; } return; } if(tr[v].add||tr[v].mul!=1) spread(v); int mid=(tr[v].l+tr[v].r)>>1; if(r<=mid){ update(2*v,l,r,mu,ad); } else{ if(l>mid){ update(2*v+1,l,r,mu,ad); } else{ update(2*v,l,mid,mu,ad); update(2*v+1,mid+1,r,mu,ad); } } tr[v].value=(tr[2*v].value+tr[2*v+1].value)%P; } void query(int v,int l,int r){ if(tr[v].l==l&&tr[v].r==r){ ans=(ans+tr[v].value)%P; return; } if(tr[v].add||tr[v].mul!=1) spread(v); int mid=(tr[v].l+tr[v].r)>>1; if(r<=mid){ query(2*v,l,r); } else{ if(l>mid){ query(2*v+1,l,r); } else{ query(2*v,l,mid); query(2*v+1,mid+1,r); } } tr[v].value=(tr[2*v].value+tr[2*v+1].value)%P; } int main(){ n=read(),m=read(),P=readLL(); for(int i=1;i<=n;++i) a[i]=readLL(); build(1,1,n); while(m--){ int x,y,op=read(); LL k; if(op==1){ x=read(),y=read(),k=readLL(); k%=P; update(1,x,y,k,0); } else if(op==2){ x=read(),y=read(),k=readLL(); k%=P; update(1,x,y,1,k); } else{ ans=0; x=read(),y=read(); query(1,x,y); printf("%lld ",ans); } } return 0; }