题解 P3373 【【模板】线段树 2】
//本题要加也要乘,所以这道题必须要有两个lazy,一个保存加法,一个保存乘法
//每次访问线段树的一个节点时,标记要先下传
//如果是乘法,那么加法的标记也要更新
//加法是+=数字*区间长度
//乘法是直接乘
//先更新乘法,再更新加法,以保证正确性
//最后不要忘了更新结点的权值
//代码极其简洁,77line没有压行,1.6k大小,极易懂
#include <cstdio>
using namespace std;
typedef long long ll;
ll n,m,mod,num,typ,x,y,k,ans;
struct nod
{
ll l,r,w,add,mul;
}s[1000005];//四倍空间以上
ll build(ll x,ll L,ll R)
{
s[x]=nod{L,R,0,0,1};//构造函数,用来让代码更简洁
if(L==R)
{
scanf("%lld",&num);
return s[x].w=num%mod;//特色建树,到了叶子节点时再输入,然后传上去
}
ll mid=(L+R)/2;
return s[x].w=(build(x*2,L,mid)+build(x*2+1,mid+1,R))%mod;
}
void down(ll x)
{//具体的标记下传
s[x*2].add=(s[x].add+s[x*2].add*s[x].mul)%mod;
s[x*2+1].add=(s[x].add+s[x*2+1].add*s[x].mul)%mod;
s[x*2].mul=(s[x*2].mul*s[x].mul)%mod;
s[x*2+1].mul=(s[x*2+1].mul*s[x].mul)%mod;
s[x*2].w=(s[x*2].w*s[x].mul+s[x].add*(s[x*2].r-s[x*2].l+1))%mod;
s[x*2+1].w=(s[x*2+1].w*s[x].mul+s[x].add*(s[x*2+1].r-s[x*2+1].l+1))%mod;
s[x].add=0;
s[x].mul=1;
}
ll query(ll x,ll L,ll R)
{
down(x);
if(s[x].l>=L&&s[x].r<=R)//包含的话直接返回
return s[x].w%mod;
ll mid=(s[x].l+s[x].r)/2;//哪边有询问就加上哪边的询问值
return ((L<=mid?query(x*2,L,R):0)+(R>mid?query(x*2+1,L,R):0))%mod;
}
void update(ll x,ll v,ll typ,ll L,ll R)
{
down(x);
if(typ==1&&s[x].l>=L&&s[x].r<=R)//包含关系
{
s[x].mul=(s[x].mul*v)%mod;
s[x].add=(s[x].add*v)%mod;
s[x].w=(s[x].w*s[x].mul)%mod;//先乘再加
return;
}
if(typ==2&&s[x].l>=L&&s[x].r<=R)
{
s[x].add=(s[x].add+v)%mod;
s[x].w=(s[x].w+s[x].add*(s[x].r-s[x].l+1))%mod;
return;
}
ll mid=(s[x].l+s[x].r)/2;
if(L<=mid)
update(x*2,v,typ,L,R);
if(R>mid)
update(x*2+1,v,typ,L,R);
s[x].w=s[x*2].w+s[x*2+1].w;//最后更新父亲节点
}
main()
{
scanf("%lld%lld%lld",&n,&m,&mod);
build(1,1,n);
for(ll i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&typ,&x,&y);
if(typ!=3)
{
scanf("%lld",&k);
update(1,k,typ,x,y);
}
else
printf("%lld\n",query(1,x,y));
}
}