题目描述
如题,已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上 xx
将某区间每一个数加上 xx
求出某区间每一个数的和
输入格式
第一行包含三个整数 n,m,pn,m,p,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。
接下来 mm 行每行包含若干个整数,表示一个操作,具体如下:
操作 11: 格式:1 x y k 含义:将区间 [x,y][x,y] 内每个数乘上 kk
操作 22: 格式:2 x y k 含义:将区间 [x,y][x,y] 内每个数加上 kk
操作 33: 格式:3 x y 含义:输出区间 [x,y][x,y] 内每个数的和对 pp 取模所得的结果
输出格式
输出包含若干行整数,即为所有操作 33 的结果。
思路:虽然多了一个区间乘法,但是还是蛮难做的。今天才发现我一开始学线段树的时候做过这道题目,但是在后面的比赛训练种,并没有接触过区间乘法的题目,就给忘的一干二净,写篇博客记录一下。
需要额外注意的地方:
①在pushdown的过程中,我们在计算左右子树和的时候,采用乘法优先的原则,即规定好segtree[root* 2].value=(segtree[root* 2].value* segtree[root].mul+segtree[root].add*(本区间长度))%p。而不是加法优先的原则。
②在对区间进行加一个数的时候,没有额外需要注意的;但是当区间乘以一个数的时候,这个区间之前加的值,也需要额外的乘以这个数字。
具体解释看代码:
这个代码完全可以当作模板了^ _ ^
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxx=1e5+100;
struct node{
int l,r;
ll sum,lazyx,lazyj;
}p[maxx<<2];
int n,m,q;
ll a[maxx];
inline void pushup(int cur)
{
p[cur].sum=(p[cur<<1].sum+p[cur<<1|1].sum)%q;
}
inline void pushdown(int cur)
{
p[cur<<1].sum=(p[cur<<1].sum*p[cur].lazyx+(ll)(p[cur<<1].r-p[cur<<1].l+1)*p[cur].lazyj)%q;
p[cur<<1|1].sum=(p[cur<<1|1].sum*p[cur].lazyx+(ll)(p[cur<<1|1].r-p[cur<<1|1].l+1)*p[cur].lazyj)%q;//子节点的和=子节点的和乘以父节点的乘法懒惰标记+父节点的加法懒惰标记*当前区间的长度
p[cur<<1].lazyj=(p[cur<<1].lazyj*p[cur].lazyx+p[cur].lazyj)%q;
p[cur<<1|1].lazyj=(p[cur<<1|1].lazyj*p[cur].lazyx+p[cur].lazyj)%q;//加法的懒惰标记需要乘以父节点的乘法懒惰标记再加上父节点的加法懒惰标记
p[cur<<1].lazyx=(p[cur<<1].lazyx*p[cur].lazyx)%q;
p[cur<<1|1].lazyx=(p[cur<<1|1].lazyx*p[cur].lazyx)%q;
p[cur].lazyj=0;
p[cur].lazyx=1;
}
inline void build(int l,int r,int cur)
{
p[cur].l=l;
p[cur].r=r;
p[cur].lazyj=0;
p[cur].lazyx=1;
if(l==r)
{
p[cur].sum=(a[l])%q;
return ;
}
int mid=l+r>>1;
build(l,mid,cur<<1);
build(mid+1,r,cur<<1|1);
pushup(cur);
}
inline void updatej(int l,int r,int cur,ll x)//更新加法
{
int L=p[cur].l;
int R=p[cur].r;
if(l<=L&&R<=r)
{
p[cur].lazyj+=x;
p[cur].lazyj%=q;
p[cur].sum+=(ll)(p[cur].r-p[cur].l+1)*x;
p[cur].sum%=q;
return ;
}
pushdown(cur);
int mid=L+R>>1;
if(r<=mid) updatej(l,r,cur<<1,x);
else if(l>mid) updatej(l,r,cur<<1|1,x);
else updatej(l,mid,cur<<1,x),updatej(mid+1,r,cur<<1|1,x);
pushup(cur);
}
inline void updatex(int l,int r,int cur,ll x)//更新乘法
{
int L=p[cur].l;
int R=p[cur].r;
if(l<=L&&R<=r)
{
p[cur].sum=(p[cur].sum*x)%q;
p[cur].lazyx=(p[cur].lazyx*x)%q;
p[cur].lazyj=(p[cur].lazyj*x)%q;
return ;
}
pushdown(cur);
int mid=L+R>>1;
if(r<=mid) updatex(l,r,cur<<1,x);
else if(l>mid) updatex(l,r,cur<<1|1,x);
else updatex(l,mid,cur<<1,x),updatex(mid+1,r,cur<<1|1,x);
pushup(cur);
}
inline ll query(int l,int r,int cur)//求和
{
int L=p[cur].l;
int R=p[cur].r;
if(l<=L&&R<=r) return p[cur].sum%q;
pushdown(cur);
int mid=L+R>>1;
if(r<=mid) return query(l,r,cur<<1)%q;
else if(l>mid) return query(l,r,cur<<1|1)%q;
else return (query(l,mid,cur<<1)%q+query(mid+1,r,cur<<1|1)%q)%q;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&q))
{
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,n,1);
int x,y,z;ll k;
while(m--)
{
scanf("%d",&x);
if(x==1)
{
scanf("%d%d%lld",&y,&z,&k);
updatex(y,z,1,k);
}
else if(x==2)
{
scanf("%d%d%lld",&y,&z,&k);
updatej(y,z,1,k);
}
else
{
scanf("%d%d",&y,&z);
printf("%lld\n",query(y,z,1)%q);
}
}
}
return 0;
}
努力加油a啊