前言
传送门 :
好久没写,线段树了,没想到被难到了
思路
区间操作 对应 懒标记
两个区间操作 即 两个懒标记
Q: 两个懒标记应该先计算哪个 ?
如果先计算加法再计算乘法,那么加法一定需要被乘数整除才行
因此先使用加法
对于每个数我们都看成
a
∗
b
+
c
a*b+c
a∗b+c
因此对于 乘法懒标记 我们可以
b
∗
m
u
l
b*mul
b∗mul
对于加法懒标记
c
∗
m
u
l
+
a
d
d
c*mul + add
c∗mul+add
然后就是不好写 QAQ
CODE
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ned '\n'
const int N = 1e5+10;
int n,p,m;
int w[N];
struct node
{
int l,r;
int sum,add,mul; ///两个懒标记
}tr[N*4];///四倍空间
void pushup(int u)
{
tr[u].sum = (tr[u<<1].sum + tr[u<<1|1].sum)%p;
}
inline void eval(node &t,int add,int mul) ///因为经常 +一个*一个
{
t.sum =((ll)t.sum * mul +(ll)(t.r - t.l +1)*add)%p;
t.mul = (ll)t.mul*mul%p;
t.add = ((ll) t.add * mul + add )%p;
}
void pushdown(int u)
{
eval(tr[u<<1],tr[u].add,tr[u].mul);
eval(tr[u<<1|1],tr[u].add,tr[u].mul);
tr[u].add = 0 ,tr[u].mul = 1;
}
void build(int u,int l,int r)
{
if(l == r)
tr[u] = {l,r,w[r],0,1};
else
{
tr[u] = {l,r,0,0,1};
int mid = l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int add,int mul)
{
if(tr[u].l>=l &&tr[u].r<=r)
eval(tr[u],add,mul); ///如果在区间内,更新
else
{
pushdown(u);
int mid = tr[u].l +tr[u].r>>1;
if(l<=mid)
modify(u<<1,l,r,add,mul);
if(r>mid)
modify(u<<1|1,l,r,add,mul);
pushup(u);
}
}
int query(int u,int l,int r)
{
if(tr[u].l >= l &&tr[u].r<=r)
return tr[u].sum;
pushdown(u);
int mid = tr[u].l +tr[u].r>>1;
int sum = 0 ;
if(l<=mid)
sum = query(u<<1,l,r);
if(r>mid)
sum = (sum+query(u<<1|1,l,r))%p;
return sum;
}
inline void solve()
{
cin>>n>>p;
for(int i=1;i<=n;i++)
cin>>w[i];
build(1,1,n);
cin>>m;
while(m -- )
{
int op,l,r,d;
cin>>op>>l>>r;
if(op == 1)
{
cin>>d;
modify(1,l,r,0,d);
}
else if(op == 2)
{
cin>>d;
modify(1,l,r,d,1);
}
else
cout<<query(1,l,r)<<ned;
}
}
int main()
{
ios::sync_with_stdio(false);
solve();
return 0;
}