线段树板子(区间加法+区间乘法)

我呃呃
这个题主要的思想就是懒标记。。。(然而我之前并没有写过,结果就因为理解出现偏差,就写了2个小时)

懒标记与你当前线段树节点没关系,是维护子树的

,,,我就认为懒标记也维护了自己。。
结果就翻车了(创造了一个线段树套队列的神奇结构。。。。。。。我太弱了(不过不算空间的话,跑的还是挺快的(最大的点1s多一点(USED O2))))

这线段树上有两个懒标记,分别是加法的和乘法的。。。。

嘛,你们看题解是不是不明白为什么叫先加再乘。。。
我也不明白。。。(再抄题解(没有复制))之后
我想明白了。。。。

你pushdown标记往下传的时候,你顺带把你的子儿子给更新一下
然后分别往这两个方向考虑

假如我们现在要对这个区间做乘法,直接把他的值跟新一下以及懒标记跟新一下
这时候不需要考虑之前加没加的情况,因为在这段区间当儿子的时候,之前加的已经算到这个区间里了
对区间做加法,同上
这时候不需要考虑之前乘没乘,因为这段区间当儿子的时候,之前乘的已经算进加法懒标记里了(就差到他的儿子去。。。。)

。。。。。
感性理解。。。
其实我觉得吧
你只要不像我一样,傻逼的把懒标记认为也维护当前点
应该这题可以随便过吧。。。。。(默写资料又增加了。。。。。)

#include<bits/stdc++.h>
#define MAXN 100005
typedef long long ll;
using namespace std;

ll n,m,mod,tp,x,y,k;
ll sum[MAXN * 4],l1[MAXN * 4],l2[MAXN * 4];//当前子树加和乘 

void build(int rt , int l , int r){
	l1[rt] = 1;
	l2[rt] = 0;
	if(l == r){cin>>sum[rt];return;}
	int mid = (l + r) >> 1;
	build(rt << 1 , l , mid);
	build((rt << 1) | 1 , mid + 1 , r);
	sum[rt] = (sum[rt << 1] + sum[(rt << 1) | 1]) % mod;
	return;
}

void init(){
	cin>>n>>m>>mod;
	build(1 , 1 , n);
}

void push_down(int rt , int l , int r){ 
	int mid = (r + l) >> 1;
	sum[rt << 1] = sum[rt << 1] * l1[rt] + (mid - l + 1) * l2[rt];
	sum[(rt << 1) | 1] = sum[(rt << 1) | 1] * l1[rt] + (r - mid) * l2[rt];
	sum[rt << 1] %= mod;
	sum[(rt << 1) | 1] %= mod;
	l1[rt << 1] = (l1[rt << 1] * l1[rt]) % mod;
	l1[(rt << 1) | 1] = (l1[(rt << 1) | 1] * l1[rt]) % mod;
	l2[rt << 1] = (l2[rt << 1] * l1[rt] + l2[rt]) % mod;
	l2[(rt << 1) | 1] = (l2[(rt << 1) | 1] * l1[rt] + l2[rt]) % mod;
	l1[rt] = 1; l2[rt] = 0;
}

void add(int rt , int l , int r){//l----r乘k 
	if(y < l || r < x)return;
	if(x <= l && r <= y){
		sum[rt] = (sum[rt] * k) % mod;
		l1[rt] = (l1[rt] * k) % mod;
		l2[rt] = (l2[rt] * k) % mod;
		return;
	}
	push_down(rt , l , r);
	int mid = (l + r) >> 1;
	add(rt << 1 , l , mid);
	add((rt << 1) | 1 , mid + 1 , r);
	sum[rt] = sum[rt << 1] + sum[(rt << 1) | 1];
	sum[rt] %= mod;
}

void add2(int rt , int  l , int r){//l---r加上k 
	if(y < l || r < x)return;
	if(x <= l && r <= y){
		sum[rt] = (sum[rt] + k * (r - l + 1)) % mod;
		l2[rt] = (l2[rt] + k) % mod;
		return;
	}
	push_down(rt , l , r);
	int mid = (l + r) >> 1;
	add2(rt << 1 , l , mid);
	add2((rt << 1) | 1 , mid + 1 , r);
	sum[rt] = sum[rt << 1] + sum[(rt << 1) | 1];
	sum[rt] %= mod;
}

int que(int rt , int l , int r){
	if(y < l || r < x)return 0;
	if(x <= l && r <= y)return sum[rt];
	push_down(rt , l , r);
	int mid = (l + r) >> 1 , zz = 0;
	zz = (zz + que(rt << 1 , l , mid)) % mod;
	zz = (zz + que((rt << 1) | 1 , mid + 1 , r)) % mod;
	return zz;
}

void solve(){
	for(int i = 1 ; i <= m ; i++){
		cin>>tp;
		if(tp == 1){
			cin>>x>>y>>k;
			add(1 , 1  , n);
		}
		if(tp == 2){
			cin>>x>>y>>k;
			add2(1 , 1  , n);
		}
		if(tp == 3){
			cin>>x>>y;
			cout<<que(1 , 1 , n) % mod<<endl;
		}
	}
}

int main(){
	init();
	solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值