线段树 区间乘法加法混合

线段树 区间乘法加法混合

本题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
细心的盆友可能发现了:
当经历了乘法运算后,原有的加法也要乘相应的数,这样才能保证精度一致。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值