/*
思路:这个题目主要是用两个延迟标记的数组,一个加一个乘来处理。
但是我们的问题是,当两个延迟标记到了 一个区间的时候我们是先加还是乘呢。
如果弄清楚这个原理,这个题目,就出来了。
大家看下这个啥 忘记叫啥了(sum[rt]+3)*2 ==sum*2+3*2,我们假设+3就是线段树加的延迟标记。
乘2就是线段树的乘的延迟标记。
我们遇到乘,就直接也同样的更新加的延迟标记,因为加如果没有的话,乘就是1,加就是0,当加来的时候
我们每次都默认先乘,当有加的时候乘的就是1所以不变,当只有成的时候加的就是0, sum*a+3*b 当a等于1
b等于0的时候,我们执行这个结果是不会改变的。当有乘延迟来的时候 就得将之前的 加的延迟标记 也得乘上这个数,
就相当于哪里 +3 也得乘二,就是( sum[rt]+3)*2 ==sum*2+3*2 这样子
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define mem(a,b) memsesum(a,b,sizeof(a))
using namespace std;
#define N 100000*4+10
typedef long long ll;
ll ssss;
ll n,p,m;
ll sum[N];//求和数组
ll add[N];//延迟标记加
ll add1[N];//乘
void PushUp(int rt)//更新
{
sum[rt]=(sum[rt*2]+sum[rt*2+1])%p;
}
void build(ll l,ll r,ll rsum)//建立线段树
{
add1[rsum]=1,add[rsum]=0;
if(l==r)
{
scanf("%lld",&sum[rsum]);
return;
}
ll m=(l+r)>>1;
build(l,m,rsum<<1);
build(m+1,r,rsum<<1|1);
PushUp(rsum);
}
void pushdown(ll rsum,ll num)
{
add[rsum<<1]=(add[rsum<<1]*add1[rsum]+add[rsum])%p;
add[rsum<<1|1]=(add[rsum<<1|1]*add1[rsum]+add[rsum])%p;
add1[rsum<<1]=(add1[rsum<<1]*add1[rsum])%p;
add1[rsum<<1|1]=(add1[rsum<<1|1]*add1[rsum])%p;
sum[rsum<<1]=(sum[rsum<<1]*add1[rsum]+(num-num/2)*add[rsum])%p;
sum[rsum<<1|1]=(sum[rsum<<1|1]*add1[rsum]+(num/2)*add[rsum])%p;
add[rsum]=0;
add1[rsum]=1;
}
void updasuma(ll rsum,ll l,ll r,ll x,ll y,ll val,ll op)
{
if(x<=l&&r<=y)
{
if(op==1)
{
add[rsum]=(add[rsum]*val)%p;
add1[rsum]=(add1[rsum]*val)%p;
sum[rsum]=(sum[rsum]*val)%p;
}
else
{
add[rsum]=(add[rsum]+val);
sum[rsum]=(sum[rsum]+(r-l+1)*val)%p;
}
return;
}
pushdown(rsum,r-l+1);
ll m=(l+r)>>1;
if(x<=m)
updasuma(rsum<<1,l,m,x,y,val,op);
if(y>m)
updasuma(rsum<<1|1,m+1,r,x,y,val,op);
PushUp(rsum);
}
void query(ll x,ll y,ll l,ll r,ll rsum)
{
if(x<=l&&y>=r)
{
ssss=(ssss%p+sum[rsum]%p)%p;
return ;
}
pushdown(rsum,r-l+1);
ll m=(l+r)>>1;
if(x<=m)
query(x,y,l,m,rsum<<1);
if(y>m)
query(x,y,m+1,r,rsum<<1|1);
}
int main()
{
scanf("%lld%lld",&n,&p);
build(1,n,1);
scanf("%lld",&m);
ll x,y,val,op;
for(ll i=0;i<m;i++)
{
scanf("%lld",&op);
if(op==1)
{
scanf("%lld%lld%lld",&x,&y,&val);
updasuma(1,1,n,x,y,val,1);
}
else if(op==2)
{
scanf("%lld%lld%lld",&x,&y,&val);
updasuma(1,1,n,x,y,val,2);
}
else
{
scanf("%lld%lld",&x,&y);
ssss=0;
query(x,y,1,n,1);
printf("%lld\n",ssss);
}
}
}
UESTC - 1597 线段树~~~区间更新 区间求和
最新推荐文章于 2018-09-28 17:46:43 发布
