线段树模板,带注释(区间加减,乘)

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
#define PII pair<int,int>
#define fi first
#define pb push_back
#define sec second
#define endl '\n'
#define INF 0x3f3f3f3f
const int N=2e5+10;
const int mod = LONG_LONG_MAX ;
//这里没影响,可以根据题目修改 
int n,m,k,t ;
int s[N] ;
struct seg{
	int v,l,r ;
	int ladd,lmul ;
}tr[4*N];
//创建一个seg结构体,储存线段树的内容,分别对应 :当前值,左区间,右区间 ,以及两个懒标记(方便区间乘法和加减) 
void pushup(int u){
	tr[u].v=tr[u<<1].v%mod+tr[u<<1|1].v%mod;
//这里是把下面的内容更新到上面来
/*
假如我一共两个数据,那一共会有3个区间,分别为:{1,2}{1,1}{2,2}
当一个区间的左右相等(l==r)时,我们会把s[l]赋值给当前的v,然后通过pushup把下面的v值变化赋给当前点 
*/ 
}
void eval(seg &t, int add, int mul){
//当使用乘法修改时add是0,当使用加法修改时,mul是1,所以不用担心影响到数据。 
	t.v = (t.v * mul + (t.r - t.l + 1) * add) % mod;
//给v值附上变化 
	t.lmul = t.lmul* mul % mod;
//记录加减乘除的情况,这样用不到的时候就不向下推,节省时间,假设我要给 [4,8]进行修改,我不需要给4到8每个数字进行更改,我只需要给储存4-8的这一段修改,如果调用,这里的懒标记就会向下释放返回正确的值 
	t.ladd = (t.ladd * mul + add) % mod;
//
}
void pushdown(int u){
//这里就是懒标记向下释放的过程 
	eval(tr[u<<1],tr[u].ladd,tr[u].lmul);
	eval(tr[u<<1|1],tr[u].ladd,tr[u].lmul);
	tr[u].ladd=0;
	tr[u].lmul=1;
//当前层释放完之后记得清理当前的储存内容,防止一个添加多次增长 
}
void build(int u,int l,int r){
	if(l==r){
		tr[u]={s[l],l,r,0,1};
//如上25-26行的注释
		return ;
//这里记得返回,因为这条路已经递归到底了,不返回程序就会一直往下走卡死 
	}
	tr[u]={0,l,r,0,1};
//这里赋予当前的点u初始的v(val)为0 
	int mid = l + r>>1;
//划分左右子树,l+r>>1 和 (l+r)/2 是一样的 
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r); 
	pushup(u);
//下面的数据更新完毕,修改当前的v值 
	return ;
}
void modifyadd(int u,int add,int l,int r){
	if(tr[u].l>=l&&tr[u].r<=r){
		eval(tr[u],add,1);
//如果区间符合条件,就直接进行数据更新操作 
		return ;
//记得 return 
	}
	pushdown(u);
//向下释放上次用懒标记储存的数据,防止重复加减等干扰 
	int mid = (tr[u].l+tr[u].r)/2;
//记得是当前点u的左右去取mid而不是你要修改的区间的 l,r,我写错好几次了 
	if(l<=mid){
		modifyadd(u<<1,add,l,r);
	}
	if(r>mid){
		modifyadd(u<<1|1,add,l,r);
	}
//判断左右两边在不在修改范围之内,如果是就进入修改 
	pushup(u);
//更新当前点的数据 
}
void modifymul(int u,int mul,int l,int r){
//	cout << tr[u].l << " " << tr[u].r << endl;
	if(tr[u].l>=l&&tr[u].r<=r){
		eval(tr[u],0,mul);
		return ;
	}
	pushdown(u);
	int mid =tr[u].l+tr[u].r >> 1;
	if(l<=mid){
		modifymul(u<<1,mul,l,r);
	}
	if(r>=mid+1){
		modifymul(u<<1|1,mul,l,r);
	}
	pushup(u);
}
//这个是区间乘法,跟上面一样 
int query(int u,int l,int r){
	if(tr[u].l>=l&&tr[u].r<=r){
		return tr[u].v%mod;
//如果整个区间在查询范围之内 ,直接返回v值 
	}
	pushdown(u);
//到这里说明当前区间有部分不是查询范围,所以要继续向左右找,要释放一下懒标记储存的内容 
	int mid=(tr[u].l+tr[u].r)>>1;
	int v=0;
	if(l<=mid)v+=query(u<<1,l,r);
	if(r>mid)v+=query(u<<1|1,l,r); 
	return v%mod;
}
signed main(){
    cin >> n >> m ;
    for(int i=1;i<=n;i++){
    	cin >> s[i];
	}
	int op ;
	int l,r,v;
	build(1,1,n);
//记得建树,建树,建树,建树!!!!!!(我忘记好几次了) 
	while(m--){
		cin >> op ;
		if(op==1){
//区间加减 
			cin >> l >> r >> v;
			modifyadd(1,v,l,r);
		}else if(op==2){
//区间乘 
			cin >> l >> r >> v;
			modifymul(1,v,l,r);
		}else{
//查询 
			cin >> l >> r ;
			cout << query(1,l,r) << endl;
		}
	}
	return 0;
}

关注永雏塔菲喵~~~~关注永雏塔菲谢谢喵~~~~~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值