[模板] 线段树区间加乘

【模板】线段树 2 - 洛谷

核心思路

注意到 (sum + add) \times k = sum \times k + add\times k

所以,分别维护标记 add 和 mul 即可

AC 代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1000000+9;
#define md %=mod
#define tp t[p]
#define ls t[p*2]
#define rs t[p*2+1]
#define s t
int mod;
struct node{
	int l,r,sum,mul,add;
};
node t[N*4];
int a[N];
void push_up(int p){
	t[p].sum = t[p*2].sum+t[p*2+1].sum;
	t[p].sum md;
}
void push_down(int pos){
	s[pos << 1].sum = (s[pos << 1].sum * s[pos].mul + s[pos].add * (s[pos << 1].r - s[pos << 1].l + 1)) % mod;
    s[pos << 1].mul = (s[pos << 1].mul * s[pos].mul) % mod;
    s[pos << 1].add = (s[pos << 1].add * s[pos].mul + s[pos].add) % mod;
    
	s[pos << 1|1].sum = (s[pos << 1|1].sum * s[pos].mul + s[pos].add * (s[pos << 1|1].r - s[pos << 1|1].l + 1)) % mod;
    s[pos << 1|1].mul = (s[pos << 1|1].mul * s[pos].mul) % mod;
    s[pos << 1|1].add = (s[pos << 1|1].add * s[pos].mul + s[pos].add) % mod;
    
    s[pos].add = 0;
    t[pos].mul = 1;
}
void build(int p,int l,int r){
	t[p].l = l;
	t[p].r = r;
	t[p].mul = 1;
	if(l == r){
		t[p].sum = a[l];
		return;
	}
	int mid = (l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	push_up(p);
}
void add(int p,int begin,int end,int l,int r,int x){
	//cout<<l<<" "<<r<<" "<<begin<<" "<<end<<endl;
	if(l == begin&&end == r){
		t[p].add += x;
		t[p].add md;
		t[p].sum += (r-l+1)*x;
		t[p].sum md;
		return;
	}
	int mid = (begin+end)/2;
	push_down(p);
	//cout<<mid<<endl;
	if(r <= mid){
		//cout<<"L"<<endl;
		add(p*2,begin,mid,l,r,x);
	}
	else if(l > mid){
		//cout<<"R"<<endl;
		add(p*2+1,mid+1,end,l,r,x);
	}
	else{
	//	cout<<"LR"<<endl;
		add(p*2,begin,mid,l,mid,x);
		add(p*2+1,mid+1,end,mid+1,r,x);
	}
	push_up(p);
}
void ChangeMul(int pos, int x, int y, int k) {
	if (x <= s[pos].l && s[pos].r <= y) {
		s[pos].add = (s[pos].add * k) % mod;
		s[pos].mul = (s[pos].mul * k) % mod;
		s[pos].sum = (s[pos].sum * k) % mod;
		return;
	}
	push_down(pos);
	int mid = (s[pos].l + s[pos].r) >> 1;
	if (x <= mid) ChangeMul(pos << 1, x, y, k);
	if (y > mid) ChangeMul(pos << 1 | 1, x, y, k);
	push_up(pos);
	return;
}
long long AskRange(int pos, int x, int y) { 
	if (x <= s[pos].l && s[pos].r <= y) {
		return s[pos].sum;
	}
	
	push_down(pos);
	long long val = 0;
	int mid = (s[pos].l + s[pos].r) >> 1;
	if (x <= mid) val = (val + AskRange(pos << 1, x, y)) % mod;
	if (y > mid) val = (val + AskRange(pos << 1 | 1, x, y)) % mod;
	return val;
}
int n,m;
signed main(){
	cin>>n>>m>>mod;
	for(int i = 1;i <= n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i = 1;i <= m;i++){
		int op;
		cin>>op;
		if(op == 1){
			int x,y,k;
			cin>>x>>y>>k;
			ChangeMul(1,x,y,k);
		}
		else if(op == 2){
			int x,y,k;
			cin>>x>>y>>k;
			add(1,1,n,x,y,k);
		}
		else{
			int x,y;
			cin>>x>>y;
			cout<<AskRange(1,x,y)%mod<<endl;
		}
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值