下面是一棵支持区间加和区间乘的线段树。
写线段树时应注意递归时下传的左右边界是否与目标节点符合,判断是否进行递归时所用的左右节点是否误用了当前节点的左右边界(而不是使用正确的目标区间左右边界)
#include
#define ll long long
using namespace std;
const ll maxn=2e5+5;
ll c,n,m,p,x,y,z,a[maxn];
ll w[maxn],lc[maxn],rc[maxn],lt[maxn],mt[maxn],cnt=1;
void build(int k,int l,int r)
{
mt[k]=1;
lt[k]=0;
if(l==r) w[k]=a[r];
else
{
ll mid=(l+r)>>1;
build(lc[k]=++cnt,l,mid);//以儿子为根继续build
build(rc[k]=++cnt,mid+1,r);
w[k]=(w[lc[k]]+w[rc[k]])%p;
}
}
void pd(ll k,ll l,ll r)
{
mt[lc[k]]=mt[lc[k]]*mt[k]%p;//下传乘法lazy tag
mt[rc[k]]=mt[rc[k]]*mt[k]%p;
lt[lc[k]]=lt[lc[k]]*mt[k]%p;//下传受乘法影响的加法lazy tag
lt[rc[k]]=lt[rc[k]]*mt[k]%p;
w[lc[k]]=w[lc[k]]*mt[k]%p;//维护区间值
w[rc[k]]=w[rc[k]]*mt[k]%p;
mt[k]=1;//乘法lazy tag重置为1
ll mid=(l+r)>>1;
w[lc[k]]=(w[lc[k]]+lt[k]*(mid-l+1))%p;//维护区间值
w[rc[k]]=(w[rc[k]]+lt[k]*(r-mid))%p;
lt[lc[k]]=(lt[lc[k]]+lt[k])%p;//下传加法lazy tag
lt[rc[k]]=(lt[rc[k]]+lt[k])%p;
lt[k]=0;//重置加法lazy tag
}
void mul(ll k,ll l,ll r,ll cl,ll cr,ll v)
{
if(cl<=l&&r<=cr)
{
mt[k]=mt[k]*v%p;//乘法lazy tag处理
lt[k]=lt[k]*v%p;//加法lazy tag会受乘法影响
w[k]=w[k]*v%p;//维护区间值
return;
}
pd(k,l,r);
ll mid=(l+r)>>1;
if(cl<=mid) mul(lc[k],l,mid,cl,cr,v);//决定是否向下递归
if(cr>mid) mul(rc[k],mid+1,r,cl,cr,v);
w[k]=(w[lc[k]]+w[rc[k]])%p; //维护区间值
}
void add(ll k,ll l,ll r,ll cl,ll cr,ll v)//与乘法操作同理
{
if(cl<=l&&r<=cr)
{
lt[k]=(lt[k]+v)%p;
w[k]=(w[k]+v*(r-l+1))%p;
return;
}
pd(k,l,r);
ll mid=(l+r)>>1;
if(cl<=mid) add(lc[k],l,mid,cl,cr,v);
if(cr>mid) add(rc[k],mid+1,r,cl,cr,v);
w[k]=(w[lc[k]]+w[rc[k]])%p;
}
ll sum(ll k,ll l,ll r,ll cl,ll cr)//与乘法操作同理
{
pd(k,l,r);
if(cl<=l&&r<=cr) return w[k];
ll mid=(l+r)>>1,ans=0;
if(cl<=mid) ans+=sum(lc[k],l,mid,cl,cr);//注意为“ans+=”而非“ans=”
if(cr>mid) ans+=sum(rc[k],mid+1,r,cl,cr);
return ans;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&p);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%lld",&c);
if(c==1)
{
scanf("%lld%lld%lld",&x,&y,&z);
mul(1,1,n,x,y,z);
}
if(c==2)
{
scanf("%lld%lld%lld",&x,&y,&z);
add(1,1,n,x,y,z);
}
if(c==3)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",sum(1,1,n,x,y)%p);
}
}
return 0;
}