线段树 区间乘法加法混合
本题AC代码:
在此之前,如果不懂线段树加法的可以看看这篇文章:线段树
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
#define ll long long
#define N 500005
ll s[N];
ll P;
struct tree
{
ll l,r,len;
ll v,color,color1;
}tre[N];
void build_tree(ll a,ll l,ll r)
{
tre[a].color=0;tre[a].color1=1;
tre[a].l=l;tre[a].r=r;tre[a].len=r-l+1;
if(tre[a].l==tre[a].r){tre[a].v=s[l]%P;return ;}
ll mid=(tre[a].l+tre[a].r)/2;
build_tree(2*a,l,mid);
build_tree(2*a+1,mid+1,r);
tre[a].v=(tre[2*a].v+tre[2*a+1].v)%P;
}
void pushdown(ll a)
{
if(tre[a].color==0&&tre[a].color1==1)return ;
tre[2*a].v=tre[2*a].v*tre[a].color1%P;
tre[2*a+1].v=tre[2*a+1].v*tre[a].color1%P;
tre[2*a].v=(tre[2*a].v+tre[a].color*tre[2*a].len)%P;
tre[2*a+1].v=(tre[2*a+1].v+tre[a].color*tre[2*a+1].len)%P;
tre[2*a].color1=(tre[a].color1*tre[2*a].color1)%P;
tre[2*a+1].color1=(tre[a].color1*tre[2*a+1].color1)%P;
tre[2*a].color=(tre[2*a].color*tre[a].color1+tre[a].color)%P;
tre[2*a+1].color=(tre[2*a+1].color*tre[a].color1+tre[a].color)%P;
tre[a].color1=1;tre[a].color=0;
}
void add_tree(ll a,ll l,ll r,ll k)
{
if(tre[a].l>=l&&tre[a].r<=r)
{
tre[a].v=(tre[a].v+tre[a].len*k)%P;
tre[a].color=(tre[a].color+k)%P;
return;
}
ll mid=(tre[a].l+tre[a].r)/2;pushdown(a);
if(l<=mid)add_tree(2*a,l,r,k);
if(r>mid)add_tree(2*a+1,l,r,k);
tre[a].v=(tre[2*a].v+tre[2*a+1].v)%P;
//cout<<tre[1].v<<' '<<tre[2].v<<' '<<tre[3].v<<' '<<tre[4].v<<' '<<tre[5].v<<' '<<tre[6].v<<' '<<tre[7].v<<' '<<tre[8].v<<' '<<tre[9].v<<endl;
}
void mul_tree(ll a,ll l, ll r,ll k)
{
if(tre[a].l>=l&&tre[a].r<=r)
{
tre[a].v=((tre[a].v%P)*(k%P))%P;
tre[a].color1=tre[a].color1*k%P;
tre[a].color=tre[a].color*k%P;
return ;
}
ll mid=(tre[a].l+tre[a].r)/2;pushdown(a);
if(l<=mid)mul_tree(2*a,l,r,k);
if(r>mid)mul_tree(2*a+1,l,r,k);
tre[a].v=(tre[2*a].v+tre[2*a+1].v)%P;
}
ll find_tree(ll a,ll l,ll r)
{
if(tre[a].l>=l&&tre[a].r<=r)return tre[a].v%P;
ll mid=(tre[a].l+tre[a].r)/2;pushdown(a);
ll ans=0;
if(l<=mid)ans=(ans%P+find_tree(2*a,l,r)%P)%P;
if(r>mid)ans=(ans%P+find_tree(2*a+1,l,r)%P)%P;
return ans%P;
}
int main()
{
ll n,m;
cin>>n>>m>>P;
for(int i=1;i<=n;i++)cin>>s[i];
build_tree(1,1,n);
ll op,ans,kase,k;
while(m--)
{
cin>>op;
if(op==2)
{
cin>>ans>>kase>>k;
add_tree(1,ans,kase,k);
}
if(op==1)
{
cin>>ans>>kase>>k;
mul_tree(1,ans,kase,k);
}
if(op==3)
{
cin>>ans>>kase;
cout<<find_tree(1,ans,kase)%P<<endl;
}
}
return 0;
}
此题和之前的线段树 区间加法相似,主要的区别和难点在于怎么处理加法和乘法的混合标记转移。
比如说 数组 :1 2 3 4 5
如果在区间2 到 4 上加 2
数组就变成:1 2+2 3+2 4+2 5
如果在区间2 到 5 上乘2
数组就变成:1 2X2+2X2 3X2+2X2 4X2+2X2 5X2
细心的盆友可能发现了:
当经历了乘法运算后,原有的加法也要乘相应的数,这样才能保证精度一致。