「模板」 线段树——区间乘 && 区间加 && 区间求和
「模板」 线段树——区间乘 && 区间加 && 区间求和
来自一个NOIP没学好的省选选手——本应早在NOIP之前学好的内容。
#include <cstdio>
const int MAXN=100010;
int n,m;
long long p;
class SegmentTree
{
public:
void Build(int i,int l,int r)
{
s[i]=node(l,r,1LL,0LL);
if(l==r)
{
scanf("%lld",&s[i].v);
return;
}
int j=i<<1,mid=l+r>>1;
Build(j,l,mid),Build(j|1,mid+1,r);
PushUp(i);
}
void Mul(int i,int l,int r,long long k)
{
if(l==s[i].l && r==s[i].r)
{
s[i].v=s[i].v*k%p;
s[i].mul=s[i].mul*k%p;
s[i].add=s[i].add*k%p;
return;
}
PushDown(i);
int j=i<<1,mid=s[i].l+s[i].r>>1;
if(r<=mid)
Mul(j,l,r,k);
else if(l>mid)
Mul(j|1,l,r,k);
else
Mul(j,l,mid,k),Mul(j|1,mid+1,r,k);
PushUp(i);
}
void Add(int i,int l,int r,long long k)
{
if(l==s[i].l && r==s[i].r)
{
s[i].v=(s[i].v+(r-l+1)*k)%p;
s[i].add=(s[i].add+k)%p;
return;
}
PushDown(i);
int j=i<<1,mid=s[i].l+s[i].r>>1;
if(r<=mid)
Add(j,l,r,k);
else if(l>mid)
Add(j|1,l,r,k);
else
Add(j,l,mid,k),Add(j|1,mid+1,r,k);
PushUp(i);
}
long long Sum(int i,int l,int r)
{
if(l==s[i].l && r==s[i].r)
return s[i].v;
PushDown(i);
int j=i<<1,mid=s[i].l+s[i].r>>1;
if(r<=mid)
return Sum(j,l,r);
else if(l>mid)
return Sum(j|1,l,r);
else
return (Sum(j,l,mid)+Sum(j|1,mid+1,r))%p;
}
private:
struct node
{
int l,r;
long long v,mul,add;
node(int _l=0,int _r=0,long long _mul=0,long long _add=0)
{
l=_l,r=_r,mul=_mul,add=_add;
}
}s[MAXN<<2];
void Update(int i,long long mul,long long add)
{
s[i].v=(s[i].v*mul+(s[i].r-s[i].l+1)*add)%p;
s[i].mul=s[i].mul*mul%p;
s[i].add=(s[i].add*mul+add)%p;
}
void PushUp(int i)
{
int j=i<<1;
s[i].v=(s[j].v+s[j|1].v)%p;
}
void PushDown(int i)
{
int j=i<<1;
Update(j,s[i].mul,s[i].add),Update(j|1,s[i].mul,s[i].add);
s[i].mul=1,s[i].add=0;
}
}T;
int main(int argc,char *argv[])
{
scanf("%d %d %lld",&n,&m,&p);
T.Build(1,1,n);
for(int i=1,opt,x,y;i<=m;++i)
{
long long k;
scanf("%d %d %d",&opt,&x,&y);
switch(opt)
{
case 1:
scanf("%lld",&k);
T.Mul(1,x,y,k);
break;
case 2:
scanf("%lld",&k);
T.Add(1,x,y,k);
break;
case 3:
printf("%lld\n",T.Sum(1,x,y));
break;
}
}
return 0;
}
谢谢阅读。