線段樹 區間乘法加法混合

愚末語發表於2020-11-17

線段樹 區間乘法加法混合

本題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
細心的盆友可能發現了:
當經歷了乘法運算後,原有的加法也要乘相應的數沒這樣才能保證精度一致。

相關文章