题目链接:https://www.luogu.org/problem/P3373
题意:线段树操作,操作一区间内每个数乘k,操作二区间每个数+k,操作三查询区间数的和(还会输入一个p,要求的是模p后的结果)
分析:要用到两个update函数,两种lazytag这个很容易想到了,但是pushdown函数的书写却很头疼,不知道一会加一会乘该怎么标记下传
我们仔细分析会发现,当进行加法操作时,加法优先级低,只改变自身lazytag即可
进行乘法操作时,乘法优先级高,会同时改变加法和乘法的lazytag
故我们在标记下传的时候先进行乘法再进行加法
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=1<<30; const int maxn=1e5+7; const double pi=acos(-1); const int mod=100; #define meminf(a) memset(a,0x3f,sizeof(a)) #define mem0(a) memset(a,0,sizeof(a)) #define ls rt<<1 #define rs rt<<1|1 #define mid (l+r)>>1 ll dat[maxn<<2],a[maxn<<2],lazyadd[maxn<<2],lazymul[maxn<<2]; int n_,m,p; void pushup(int rt){ dat[rt]=(dat[ls]+dat[rs])%p; } void pushdown(int rt,int l,int r){ dat[ls]=((dat[ls]*lazymul[rt])%p+lazyadd[rt]*(r-l)/2+p)%p; dat[rs]=((dat[rs]*lazymul[rt])%p+lazyadd[rt]*(r-l)/2+p)%p; lazymul[ls]=(lazymul[rt]*lazymul[ls])%p; lazymul[rs]=(lazymul[rt]*lazymul[rs])%p; lazyadd[ls]=((lazymul[rt]*lazyadd[ls])%p+lazyadd[rt]+p)%p; lazyadd[rs]=((lazymul[rt]*lazyadd[rs])%p+lazyadd[rt]+p)%p; lazyadd[rt]=0; lazymul[rt]=1; } void build(int l,int r,int rt){ lazymul[rt]=1,lazyadd[rt]=0; if(l==r-1){ dat[rt]=a[l]; return; } build(l,mid,ls); build(mid,r,rs); pushup(rt); } //我们设的乘法优先级高于加法,所以这里要一并更新加法的 void update1(int l,int r,int s,int t,int rt,ll c){ if(s<=l&&r<=t){ dat[rt]=(dat[rt]*c)%p; lazymul[rt]=(lazymul[rt]*c)%p; lazyadd[rt]=(lazyadd[rt]*c)%p; return;//这里是关键 } pushdown(rt,l,r);//递归到子节点前先标记下传 if(s<mid) update1(l,mid,s,t,ls,c); if(t>mid) update1(mid,r,s,t,rs,c); pushup(rt); } void update2(int l,int r,int s,int t,int rt,ll c){ if(s<=l&&r<=t){ dat[rt]=(dat[rt]+c*(r-l)+p)%p; lazyadd[rt]=(lazyadd[rt]+c+p)%p; return;//这里是关键 } pushdown(rt,l,r);//递归到子节点前先标记下传 if(s<mid) update2(l,mid,s,t,ls,c); if(t>mid) update2(mid,r,s,t,rs,c); pushup(rt); } ll query(int l,int r,int s,int t,int rt){ ll ans=0; if(s<=l&&r<=t){ return (dat[rt]+p)%p; } pushdown(rt,l,r); if(s<mid) ans=(ans+query(l,mid,s,t,ls))%p; if(t>mid) ans=(ans+query(mid,r,s,t,rs))%p; return ans; } int main(){ scanf("%d%d%d",&n_,&m,&p); int n=1; for(int i=1;i<=n_;i++)scanf("%lld",&a[i]); while(n<n_)n<<=1; build(1,n+1,1); for(int i=1;i<=m;i++){ int opt,x,y;ll k; scanf("%d%d%d",&opt,&x,&y); if(opt==1){ scanf("%lld",&k); update1(1,n+1,x,y+1,1,k); } else if(opt==2){ scanf("%lld",&k); update2(1,n+1,x,y+1,1,k); } else if(opt==3){ printf("%lld ",query(1,n+1,x,y+1,1)%p); } } return 0; }