(线段树 + add懒标记 + mul懒标记)AcWing - 维护序列

1|0原题链接:

AcWing - 维护序列

2|0题目描述:

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。
有长为 N 的数列,不妨设为 a1, a2, …, aN。
有如下三种操作形式:
1、把数列中的一段数全部乘一个值;
2、把数列中的一段数全部加一个值;
3、询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。
输入格式
第一行两个整数 N 和 P;
第二行含有 N 个非负整数,从左到右依次为 a1,a2,…,aN;
第三行有一个整数 M,表示操作总数;
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
 操作 1:1 t g c,表示把所有满足 t≤i≤g 的 ai 改为 ai×c;
 操作 2:2 t g c,表示把所有满足 t≤i≤g 的 ai 改为 ai+c;
 操作 3:3 t g,询问所有满足 t≤i≤g 的 ai 的和模 P 的值。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
输出格式
对每个操作 3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
数据范围
1≤N,M≤1e5,
1≤t≤g≤N,
0≤c,ai≤1e9,
1≤P≤1e9
输入样例:

7 43 1 2 3 4 5 6 7 5 1 2 5 5 3 2 4 2 3 7 9 3 1 3 3 4 7

输出样例:

2 35 8

3|0思路:

  线段树中的值有l, r, sum,这是肯定的,然后还有懒标记add和mul,
那么根据懒标记更新sum时是先乘,还是先加呢,那就难点来了,怎样推出个公式呢:

对于x 若对懒标记的处理是先加再乘 若此次操作为乘上一个数c 可以表示为 (n + add) * mul * c 即 (n + X) * X 的形式 若此次操作为加上一个数c (n + add) * mul + c 不能写成 (n + X ) * X的形式 -> 无法更新新的懒标记 若对懒标记的处理是先乘再加 若此次操作是加上一个数c 可以表示为n * mul + add + c -> 此时新的add即为add + c 若此次操作是乘上一个数c 可以表示为n * mul * c + add * c -> 此时新的add即为add * c,新的mul即为mul * c -> 故先乘再加,以便更新懒标记 可以把乘和加的操作都看成 x * c + d -> 若是乘法,d为0 -> 若是加法,c为1 若当前x的懒标记为add和mul -> 操作可以写成(x * mul + add) * c + d -> 即x * (mul * c) + (add * c + d) -> 新的mul为(mul * c),新的add为(add * c + d) 注意:乘的懒标记初始为1 作者:bigstone 链接:https://www.acwing.com/solution/content/43107/

  既然公式推出来了,那么我们就能愉快的写题了,和add懒标记的模板差不多.

#include<bits/stdc++.h> using namespace std; #define endl '\n' #define int long long typedef pair<int, int> PII; const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; const int N = 1e5 + 10; int n, p, m; int w[N]; struct Node{ int l, r; int sum, add, mul; }tr[N << 2]; void pushup(int u){ tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % p; } void eval(Node &t, int mul, int add){ t.sum = (t.sum * mul % p + (t.r - t.l + 1) * add) % p; t.mul = t.mul * mul % p; t.add = (t.add * mul + add) % p; } void pushdown(int u){ eval(tr[u << 1], tr[u].mul, tr[u].add); eval(tr[u << 1 | 1], tr[u].mul, tr[u].add); tr[u].mul = 1, tr[u].add = 0; } void build(int u, int l, int r){ tr[u].l = l, tr[u].r = r; tr[u].mul = 1; if(l == r) tr[u].sum = w[l]; else{ int mid = l + r >> 1; build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r); pushup(u); } } void modify(int u, int l, int r, int add, int mul){ if(tr[u].l >= l && tr[u].r <= r) eval(tr[u], mul, add); else{ pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if(l <= mid) modify(u << 1, l, r, add, mul); if(r > mid) modify(u << 1 | 1, l, r, add, mul); pushup(u); } } int query(int u, int l, int r){ if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum; pushdown(u); int mid = tr[u].l + tr[u].r >> 1; int res = 0; if(l <= mid) res = query(u << 1, l, r); if(r > mid) res = (res + query(u << 1 | 1, l, r)) % p; return res; } signed main(void){ ios::sync_with_stdio(false); cin.tie(0); cin >> n >> p; for(int i = 1; i <= n; i++) cin >> w[i]; build(1, 1, n); cin >> m; for(int i = 1; i <= m; i++){ int op, l, r, d; cin >> op >> l >> r; if(op == 1){ cin >> d; modify(1, l, r, 0, d); } else if(op == 2){ cin >> d; modify(1, l, r, d, 1); } else cout << query(1, l, r) << endl; } return 0; }

__EOF__

本文作者ReSakura
本文链接https://www.cnblogs.com/ReSakura/p/16216505.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ReSakura  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示