线段树-模板

透彻线段树


1.

区间加

#include <bits/stdc++.h>
using namespace std;
/*
如果人可以长尾巴

会觉得有点难为情呢

因为只要和你在一起,我总会忍不住摇尾巴吧
*/
const int maxn = 1e6 + 10;

#define ls(now) (now << 1)
#define rs(now) (now<<1|1)
#define mid ((l + r) >> 1)

int n, m, a[maxn];

struct seg_tree{
	struct nodes{
		long long l, r, sum, tag;
		long long get(){
			return sum + (r - l + 1) * tag;
		}
	}node[maxn];
	void up(int now){
		return (void)(node[now].sum = node[ls(now)].get() + node[rs(now)].get());
	}
	void down(int now){
		return (void)(node[ls(now)].tag += node[now].tag, node[rs(now)].tag += node[now].tag, node[now].tag = 0);
	}
	void bulid(int l, int r, int now){
		node[now].l = l, node[now].r = r;
		if(l == r) return (void)(node[now].sum = a[l]);
		bulid(l, mid, ls(now)), bulid(mid+1, r, rs(now));
		up(now);
	}
	void chenge(int l, int r, int now, int val){
		if(node[now].r < l or node[now].l > r) return;
		if(l <= node[now].l and node[now].r <= r) return (void)(node[now].tag += val);
		down(now);
		chenge(l, r, ls(now), val), chenge(l, r, rs(now), val);
		up(now);
	}
	void query(int l, int r, int now, long long &ans){
		if(l > node[now].r or r < node[now].l) return;
		if(l <= node[now].l and node[now].r <= r) return (void)(ans += node[now].get());
		down(now);
		query(l, r, ls(now), ans), query(l, r, rs(now), ans);
		up(now);
	}

}tree;

signed main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a[i]);
	}
	tree.bulid(1, n, 1);
	for(int cmp, x, y, z; m ; m --){
		scanf("%d", &cmp);
		if(cmp == 1){
			scanf("%d%d%d", &x, &y, &z);
			tree.chenge(x, y, 1, z);
		}
		if(cmp == 2){
			long long ans = 0;
			scanf("%d%d", &x, &y);
			tree.query(x, y, 1, ans);
			printf("%lld\n", ans);
		}
	}
	return 0;
}

2.

区间乘+区间加

加法和乘法顺序不一样会导致不同的结果

比如: (a+b)c 不等于 ac+b

而在记录懒标记的时候,加法和乘法两种标记放到一起,并不知道哪个先,哪个后。

所以要确定一个优先级

我们分析一下两种顺序:

  1. 先加后乘 : (a+b)c = ac+bc

  2. 先乘后加:ac+b

比较一下,发现,上面的先加后乘相当于下面的式子,在加法上面多乘了一个c

所以,我们只要是先加后乘的式子,只要加一个c就可以转化为先乘后加的式子

具体的操作就是在添加乘法标记的时候,把加法标记c就好了

所以,我们就定了一个总顺序:先乘后加(摘自luogu博客)

然后在down下放标记的时候,左右儿子的加法标记传递也要保持先乘后加的顺序,即 :

void down(int now){
	t[ls(now)].tmp = (t[ls(now)].tmp*t[now].tmp)%p;
	t[rs(now)].tmp = (t[rs(now)].tmp*t[now].tmp)%p; 
	t[ls(now)].tag = (t[ls(now)].tag*t[now].tmp)%p;
	t[rs(now)].tag = (t[rs(now)].tag*t[now].tmp)%p; 
	t[ls(now)].tag = (t[ls(now)].tag+t[now].tag)%p;
	t[rs(now)].tag = (t[rs(now)].tag+t[now].tag)%p;
	t[now].tag = 0, t[now].tmp = 1;
	return;
}

解决方法:先乘后加

//对于每一次的区间乘val,我们对在此次操作前做的区间加也乘上这次的val
t[now].tmp *= val, t[now].tag *= val

完美的解决了问题

#include <bits/stdc++.h>
using namespace std;
/*
如果人可以长尾巴

会觉得有点难为情呢

因为只要和你在一起,我总会忍不住摇尾巴吧
*/
const int maxn = 1e6 + 10;

#define ls(now) (now << 1)
#define rs(noe) (now<<1|1)
#define mid ((l + r) >> 1)

int n, m, p, a[maxn];
long long ans;

struct seg_tree{

	struct nodes{
		long long l, r, sum, tag, tmp;
		nodes(){
			tmp = 1;
			tag = 0;
		}
		long long get(){
			return (((sum%p)*tmp%p)%p + ((r-l+1)*tag)%p);
		}
	}t[maxn];

	void up(int now){
		return (void)(t[now].sum = t[ls(now)].get() + t[rs(now)].get());
	}
	void down(int now){
		t[ls(now)].tmp = (t[ls(now)].tmp*t[now].tmp)%p;
		t[rs(now)].tmp = (t[rs(now)].tmp*t[now].tmp)%p; 
		t[ls(now)].tag = (t[ls(now)].tag*t[now].tmp)%p;
		t[rs(now)].tag = (t[rs(now)].tag*t[now].tmp)%p; 
		t[ls(now)].tag = (t[ls(now)].tag+t[now].tag)%p;
		t[rs(now)].tag = (t[rs(now)].tag+t[now].tag)%p;
		t[now].tag = 0, t[now].tmp = 1;
		return;
	}
	void bulid(int l, int r, int now){
		t[now].l = l, t[now].r = r;
		if(l == r) return (void)(t[now].sum = a[l]);
		bulid(l, mid, ls(now)), bulid(mid+1, r, rs(now));
		up(now);
	}
	void chenge(int l, int r, int now, int val){
		if(r < t[now].l or l > t[now].r) return;
		if(l <= t[now].l and t[now].r <= r) return (void)(t[now].tag += val);
		down(now);
		chenge(l, r, ls(now), val), chenge(l, r, rs(now), val);
		up(now);
	}
	void change(int l, int r, int now, int val){
		if(r < t[now].l or l > t[now].r) return;
		if(l <= t[now].l and t[now].r <= r) return (void)(t[now].tmp *= val, t[now].tag *= val);
		down(now);
		change(l, r, ls(now), val), change(l, r, rs(now), val);
		up(now);
	}
	void query(int l, int r, int now, long long &ans){
		if(r < t[now].l or l > t[now].r) return;
		if(l <= t[now].l and t[now].r <= r) return (void)(ans += t[now].get(), ans %= p);
		down(now);
		query(l, r, ls(now), ans), query(l, r, rs(now), ans);
		up(now);
	}
}tree;

signed main(){
	scanf("%d%d%d", &n, &m, &p);
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a[i]);
	}
	tree.bulid(1, n, 1);
	for(int cmp, x, y, z; m; m --){
		scanf("%d", &cmp);
		if(cmp == 1){
			scanf("%d%d%d", &x, &y, &z);
			tree.change(x, y, 1, z);
		}
		if(cmp == 2){
			scanf("%d%d%d", &x, &y, &z);
			tree.chenge(x, y, 1, z);
		}
		if(cmp == 3){
			scanf("%d%d", &x, &y);
			tree.query(x, y, 1, ans);
			printf("%lld\n", ans);
			ans = 0;
		}
	}
	return 0;
}

本文作者:Vanyun

本文链接:https://www.cnblogs.com/Vanyun/p/13283391.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Vanyun  阅读(98)  评论(3编辑  收藏  举报
编辑推荐:
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
阅读排行:
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法
· Vite CVE-2025-30208 安全漏洞
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.

作曲 : Reol

作词 : Reol

fade away...do over again...

fade away...do over again...

歌い始めの一文字目 いつも迷ってる

歌い始めの一文字目 いつも迷ってる

どうせとりとめのないことだけど

伝わらなきゃもっと意味がない

どうしたってこんなに複雑なのに

どうしたってこんなに複雑なのに

噛み砕いてやらなきゃ伝わらない

ほら結局歌詞なんかどうだっていい

僕の音楽なんかこの世になくたっていいんだよ

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.

目の前 広がる現実世界がまた歪んだ

目の前 広がる現実世界がまた歪んだ

何度リセットしても

僕は僕以外の誰かには生まれ変われない

「そんなの知ってるよ」

気になるあの子の噂話も

シニカル標的は次の速報

麻痺しちゃってるこっからエスケープ

麻痺しちゃってるこっからエスケープ

遠く遠くまで行けるよ

安定なんてない 不安定な世界

安定なんてない 不安定な世界

安定なんてない きっと明日には忘れるよ

fade away...do over again...

fade away...do over again...

そうだ世界はどこかがいつも嘘くさい

そうだ世界はどこかがいつも嘘くさい

綺麗事だけじゃ大事な人たちすら守れない

くだらない 僕らみんなどこか狂ってるみたい

本当のことなんか全部神様も知らない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.