#include <cstdio>
#define lson rt<<1,l,mid//这个#define就很有用处了
#define rson rt<<1|1,mid+1,r
#define Maxn 500000
#define ll long long
#define lld lld
using namespace std;
ll seg[Maxn];//记录区间i的和
ll marka[Maxn];//记录 i区间要加的数
ll markm[Maxn];//记录 i区间乘的数
ll mod;//记录模数
void pushup(ll rt)
{
seg[rt]=(seg[rt<<1]+seg[rt<<1|1])%mod;//已知两个子区间各自的和 计算其父亲区间的和
}
void pushdown(ll rt,ll x)//下传
{
marka[rt<<1]=(marka[rt]+marka[rt<<1]*markm[rt])%mod;//lazy1 下放标记值
marka[rt<<1|1]=(marka[rt]+marka[rt<<1|1]*markm[rt])%mod;
markm[rt<<1]=(markm[rt]*markm[rt<<1])%mod;//lazy2 下放标记值
markm[rt<<1|1]=(markm[rt]*markm[rt<<1|1])%mod;
//rt的和在进行下放前已经被更新了
seg[rt<<1]=(marka[rt]*(x-x/2)+seg[rt<<1]*markm[rt])%mod;//更新 已被下放的区间的和
seg[rt<<1|1]=(marka[rt]*(x/2)+seg[rt<<1|1]*markm[rt])%mod;
marka[rt]=0;//清空
markm[rt]=1;//清空
}
void build(ll rt,ll l,ll r)//建树
{
markm[rt]=1;//初始化乘值
if (l==r)
scanf("%lld",&seg[rt]);
else
{
ll mid=(l+r)/2;
build(lson);
build(rson);
pushup(rt);//到这一步时 rt两个子区间的和已经出来了 求rt的和
}
}
ll query(ll rt,ll l,ll r,ll L,ll R)
{
if (l>=L && r<=R)//若符合的话 rt这个区间就是要求的区间的子区间
return seg[rt];//直接返回 这个区间的和 这也是为什么要预先处理被标记的区间的和
else
{
pushdown(rt,r-l+1);//下放
ll mid=(r+l)/2,ans=0;
if (mid>=L)
ans=(ans+query(lson,L,R))%mod;//左区间和要求的区间有交集
if (mid<R)
ans=(ans+query(rson,L,R))%mod;//右区间和要求的区间有交集
return ans%mod;
}
}
void update_a(ll rt,ll l,ll r,ll L,ll R,ll x)
{
if (l>=L && r<=R)
seg[rt]=(seg[rt]+(r-l+1)*x)%mod,marka[rt]=(x+marka[rt])%mod;
else
{
pushdown(rt,r-l+1);
ll mid=(r+l)/2;
if (mid>=L)
update_a(lson,L,R,x);
if (mid<R)
update_a(rson,L,R,x);
pushup(rt);
}
}
void update_m(ll rt,ll l,ll r,ll L,ll R,ll x)
{
if (l>=L && r<=R)
seg[rt]=(seg[rt]*x)%mod,marka[rt]=(x*marka[rt])%mod,markm[rt]=(x*markm[rt])%mod;
else
{
pushdown(rt,r-l+1);
ll mid=(r+l)/2;
if (mid>=L)
update_m(lson,L,R,x);
if (mid<R)
update_m(rson,L,R,x);
pushup(rt);
}
}
main()
{
ll n,m,x,y,z,c;
scanf("%lld%lld%lld",&n,&m,&mod);
build(1,1,n);
while(m--)
{
scanf("%lld",&c);
if(c==1)//乘值
{
scanf("%lld%lld%lld",&x,&y,&z);
update_m(1,1,n,x,y,z);//找到被包含的区间 更新和值并标记
}
else
if(c==2)//加值
{
scanf("%lld%lld%lld",&x,&y,&z);
update_a(1,1,n,x,y,z);//同上
}
else
if (c==3)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,1,n,x,y));//输出
}
}
#define lson rt<<1,l,mid//这个#define就很有用处了
#define rson rt<<1|1,mid+1,r
#define Maxn 500000
#define ll long long
#define lld lld
using namespace std;
ll seg[Maxn];//记录区间i的和
ll marka[Maxn];//记录 i区间要加的数
ll markm[Maxn];//记录 i区间乘的数
ll mod;//记录模数
void pushup(ll rt)
{
seg[rt]=(seg[rt<<1]+seg[rt<<1|1])%mod;//已知两个子区间各自的和 计算其父亲区间的和
}
void pushdown(ll rt,ll x)//下传
{
marka[rt<<1]=(marka[rt]+marka[rt<<1]*markm[rt])%mod;//lazy1 下放标记值
marka[rt<<1|1]=(marka[rt]+marka[rt<<1|1]*markm[rt])%mod;
markm[rt<<1]=(markm[rt]*markm[rt<<1])%mod;//lazy2 下放标记值
markm[rt<<1|1]=(markm[rt]*markm[rt<<1|1])%mod;
//rt的和在进行下放前已经被更新了
seg[rt<<1]=(marka[rt]*(x-x/2)+seg[rt<<1]*markm[rt])%mod;//更新 已被下放的区间的和
seg[rt<<1|1]=(marka[rt]*(x/2)+seg[rt<<1|1]*markm[rt])%mod;
marka[rt]=0;//清空
markm[rt]=1;//清空
}
void build(ll rt,ll l,ll r)//建树
{
markm[rt]=1;//初始化乘值
if (l==r)
scanf("%lld",&seg[rt]);
else
{
ll mid=(l+r)/2;
build(lson);
build(rson);
pushup(rt);//到这一步时 rt两个子区间的和已经出来了 求rt的和
}
}
ll query(ll rt,ll l,ll r,ll L,ll R)
{
if (l>=L && r<=R)//若符合的话 rt这个区间就是要求的区间的子区间
return seg[rt];//直接返回 这个区间的和 这也是为什么要预先处理被标记的区间的和
else
{
pushdown(rt,r-l+1);//下放
ll mid=(r+l)/2,ans=0;
if (mid>=L)
ans=(ans+query(lson,L,R))%mod;//左区间和要求的区间有交集
if (mid<R)
ans=(ans+query(rson,L,R))%mod;//右区间和要求的区间有交集
return ans%mod;
}
}
void update_a(ll rt,ll l,ll r,ll L,ll R,ll x)
{
if (l>=L && r<=R)
seg[rt]=(seg[rt]+(r-l+1)*x)%mod,marka[rt]=(x+marka[rt])%mod;
else
{
pushdown(rt,r-l+1);
ll mid=(r+l)/2;
if (mid>=L)
update_a(lson,L,R,x);
if (mid<R)
update_a(rson,L,R,x);
pushup(rt);
}
}
void update_m(ll rt,ll l,ll r,ll L,ll R,ll x)
{
if (l>=L && r<=R)
seg[rt]=(seg[rt]*x)%mod,marka[rt]=(x*marka[rt])%mod,markm[rt]=(x*markm[rt])%mod;
else
{
pushdown(rt,r-l+1);
ll mid=(r+l)/2;
if (mid>=L)
update_m(lson,L,R,x);
if (mid<R)
update_m(rson,L,R,x);
pushup(rt);
}
}
main()
{
ll n,m,x,y,z,c;
scanf("%lld%lld%lld",&n,&m,&mod);
build(1,1,n);
while(m--)
{
scanf("%lld",&c);
if(c==1)//乘值
{
scanf("%lld%lld%lld",&x,&y,&z);
update_m(1,1,n,x,y,z);//找到被包含的区间 更新和值并标记
}
else
if(c==2)//加值
{
scanf("%lld%lld%lld",&x,&y,&z);
update_a(1,1,n,x,y,z);//同上
}
else
if (c==3)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(1,1,n,x,y));//输出
}
}
}
程序转自http://www.yhzq-blog.cc/%e7%ba%bf%e6%ae%b5%e6%a0%91%e9%ab%98%e9%98%b6%e6%80%bb%e7%bb%93/
不过 略加修改