线段树模板

#include<bits/stdc++.h>
#define ll long long
using namespace std;
unsigned ll a[1000001],t[1000001 << 2],ans[1000001 << 2],n,m,o,t2[1000001 << 2];
inline ll lc(ll x){//left chrildren 左儿子 
	return x << 1;
}
inline ll rc(ll x){//right chrildren 右儿子
	return x << 1 | 1;//二进制位左移一位代表着数值*2,而如果左移完之后再或上1,由于左移完之后最后一位二进制位上一定会是0,所以|1等价于+1。
}
inline void push_up(ll p){//	向上不断维护区间操作 
	ans[p] = (ans[lc(p)] + ans[rc(p)]) % o;
}
inline void push_up1(ll p){
	ans[p] = ans[lc(p)] + ans[rc(p)];
}
void build(ll p,ll l,ll r){
	t[p] = 0;//加法懒惰数组 
	t2[p] = 1;//乘法懒惰数组 
	if(l == r){
		ans[p] = a[l];
		return ;
	}
	ll mid = (l + r) >> 1;
	build(lc(p),l,mid);
	build(rc(p),mid + 1,r);
	push_up(p);//维护 
	ans[p] %= o;
	return ; 
}
inline void f(ll p,ll l,ll r,ll k){//我们可以认识到,f函数的唯一目的,就是记录当前节点所代表的区间,然后区间加 
	t[p] = t[p] + k;
	ans[p] = ans[p] + (r - l + 1) * k;
	//cout << ans[p] << "aaa" << endl;
	//由于是这个区间统一改变,所以ans数组要加元素个数次啦 
}
inline void push_down(ll p,ll l,ll r){//每次更新两个子节点,然后继续传递 
	ll mid = (l + r) >> 1;
	ans[lc(p)] = (ans[lc(p)] * t2[p] + t[p] * (mid - l + 1)) % o;//向左儿子传结果
	ans[rc(p)] = (ans[rc(p)] * t2[p] + t[p] * (r - (mid + 1) + 1)) % o;//向右儿子传结果
	t2[lc(p)] = (t2[lc(p)] * t2[p]) % o;//向左儿子传乘法tag
	t2[rc(p)] = (t2[rc(p)] * t2[p]) % o;//向右儿子传乘法tag
	t[lc(p)] = (t[lc(p)] * t2[p] + t[p]) % o;//向左儿子传加法tag
	t[rc(p)] = (t[rc(p)] * t2[p] + t[p]) % o;//向右儿子传加法tag
	t[p] = 0;
	t2[p] = 1;
}
inline void upmul(ll nl,ll nr,ll l,ll r,ll p,ll k){ //l,r为要修改的区间,nl,nr,p为当前节点所存储的区间以及节点的编号 ,区间乘法 
	if(nl <= l && r <= nr){
		ans[p] = (ans[p] * k) % o;
		t2[p] = (t2[p] * k) % o;
		t[p] = (t[p] * k) % o;
		return;
	}
	push_down(p,l,r);
	ll mid = (l + r) >> 1;
	if(nl <= mid){
		upmul(nl,nr,l,mid,lc(p),k);
	}
	if(nr > mid){
		upmul(nl,nr,mid + 1,r,rc(p),k);
	}
	push_up(p);
}
inline void upadd(ll nl,ll nr,ll l,ll r,ll p,ll k){ //l,r为要修改的区间,nl,nr,p为当前节点所存储的区间以及节点的编号 ,区间加法 
	if(nl <= l && r <= nr){
		ans[p] += (r - l + 1) * k;
		ans[p] %= o;
		t[p] += k;
		t[p] %= o;
		return;
	}
	push_down(p,l,r);
	ll mid = (l + r) >> 1;
	if(nl <= mid){
		upadd(nl,nr,l,mid,lc(p),k);
	}
	if(nr > mid){
		upadd(nl,nr,mid + 1,r,rc(p),k);
	}
	push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p){//区间求和,[q_x,q_y]为目标区间,[l,r]为当前区间 ,小心函数内部变量重复 
	ll sum = 0;
	if(q_x <= l && r <= q_y){
		return ans[p];
	}
	ll mid = (l + r) >> 1;
	push_down(p,l,r);
	if(q_x <= mid){
		sum += query(q_x,q_y,l,mid,lc(p));
		sum %= o;
	}
	if(q_y > mid){
		sum += query(q_x,q_y,mid + 1,r,rc(p));
		sum %= o;
	}
	sum %= o;
	return sum;
}
int main(){
	cin >> n >> m >> o;
	for(int i = 1;i <= n;i++){
		scanf("%lld",&a[i]);
	}
	build(1,1,n);
	for(int i = 1;i <= m;i++){
		ll zz = 0,xx = 0,yy = 0,nm = 0;
		scanf("%lld",&zz);
		if(zz == 2){
			scanf("%lld %lld %lld",&xx,&yy,&nm);
			upadd(xx,yy,1,n,1,nm);
		}else if(zz == 3){
			scanf("%lld %lld",&xx,&yy);
			printf("%lld\n",query(xx,yy,1,n,1));
		}else if(zz == 1){
			scanf("%lld %lld %lld",&xx,&yy,&nm);
			upmul(xx,yy,1,n,1,nm);
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值