一、单点修改 区间查询
例: 已知一个数列,有两种操作:
1、在数列末尾插入一个数
2、查询当前数列中末尾L个数中的最大值
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
struct segmentTree {
int l, r;
int v;
} tr[4 * N];
void build(int u, int l, int r) {
tr[u] = {l, r};
if(l == r) return ;
int mid = l + (r - l) / 2;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int i, int x) {
if(tr[u].l == i && tr[u].r == i) {
tr[u].v = x;
} else {
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
if(i <= mid) modify(u << 1, i, x);
else modify(u << 1 | 1, i, x);
tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
}
}
int query(int u, int l, int r) {
if(l <= tr[u].l && tr[u].r <= r) return tr[u].v;
else {
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
int Max = INT_MIN;
if(l <= mid) Max = max(Max, query(u << 1, l, r));
if(r > mid) Max = max(Max, query(u << 1 | 1, l, r));
return Max;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt, mod, len = 0, Last = 0;
cin >> tt >> mod;
build(1, 1, tt);
while(tt --) {
char op;
cin >> op;
if(op == 'A') {
int x;
cin >> x;
modify(1, len + 1, ((LL) Last + x) % mod);
len ++;
} else {
int i;
cin >> i;
Last = query(1, len - i + 1, len);
cout << Last << '\n';
}
}
return 0;
}
二、区间修改、区间查询(懒标记)
懒标记的核心思想在于:
1、延迟更新:
当需要对某个区间进行更新时,不立即更新所有相关节点,而是将更新信息暂时存储在懒标记中。
只有在后续查询或更新需要访问这些节点时,才将懒标记中的更新信息应用到子节点。
2、减少操作次数:
通过懒标记,避免了不必要的递归更新,减少了时间复杂度。
例如,在区间更新时,原本需要更新多个节点,现在只需更新当前节点并设置懒标记,后续需要时再传递更新。
3、标记传递:
当访问带有懒标记的节点时,先将标记传递给子节点,并更新子节点的值,然后清除当前节点的标记。
比如有一个操作将区间[4,5]的每个数都加上2,那懒标记add先只传递到[4,5]区间,若之后有查询操作查询区间[4,4]或区间[5,5]中的数的总和时,再将懒标记add向下传递。
例: 已知一个数列,有两种操作:
1、将某区间每一个数加上x
2、求出某区间每一个数的和
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, a[N];
struct segmentTree {
int l, r;
LL sum, lazy;
} tr[N * 4];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u) {
auto& root = tr[u], & left = tr[u << 1], & right = tr[u << 1 | 1];
if(root.lazy) {
left.lazy += root.lazy;
left.sum += (LL) (left.r - left.l + 1) * root.lazy;
right.lazy += root.lazy;
right.sum += (LL) (right.r - right.l + 1) * root.lazy;
root.lazy = 0;
}
}
void build(int u, int l, int r) {
if(l == r) {
tr[u] = {l, r, a[l], 0};
} else {
tr[u] = {l, r};
int mid = l + (r - l) / 2;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r, int x) {
if(l <= tr[u].l && tr[u].r <= r) {
tr[u].sum += (LL) (tr[u].r - tr[u].l + 1) * x;
tr[u].lazy += x;
} else {
pushdown(u);
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
if(l <= mid) modify(u << 1, l, r, x);
if(r > mid) modify(u << 1 | 1, l, r, x);
pushup(u);
}
}
LL query(int u, int l, int r) {
if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
else {
pushdown(u);
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
LL sum = 0;
if(l <= mid) sum = query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt;
cin >> n >> tt;
for(int i = 1; i <= n; i ++) cin >> a[i];
build(1, 1, n);
while(tt --) {
char op;
cin >> op;
if(op == '2') {
int l, r;
cin >> l >> r;
cout << query(1, l, r) << '\n';
} else {
int l, r, x;
cin >> l >> r >> x;
modify(1, l, r, x);
}
}
return 0;
}
P3373 【模板】线段树 2
该问题相比于 P3372 【模板】线段树 1 多了一个乘法操作,需要考虑乘法与加法的先后顺序
应该先乘再加,如:
tr[u << 1].sum = ((LL) tr[u << 1].sum * tr[u].mul + tr[u].add * k1) % mod
tr[u << 1].add = ((LL) tr[u << 1].add * tr[u].mul + tr[u].add) % mod
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, tt, a[N], mod;
struct segmentTree {
int l, r;
LL sum, add, mul;
} tr[4 * N];
void pushup(int u) {
tr[u].sum = (tr[u << 1].sum + tr[u << 1 | 1].sum) % mod;
}
void pushdown(int u) {
int k1 = tr[u << 1].r - tr[u << 1].l + 1;
int k2 = tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1;
tr[u << 1].sum = ((LL) tr[u << 1].sum * tr[u].mul + tr[u].add * k1) % mod;
tr[u << 1 | 1].sum = ((LL) tr[u << 1 | 1].sum * tr[u].mul + tr[u].add * k2) % mod;
tr[u << 1].mul = (LL) tr[u << 1].mul * tr[u].mul % mod;
tr[u << 1 | 1].mul = (LL) tr[u << 1 | 1].mul * tr[u].mul % mod;
tr[u << 1].add = ((LL) tr[u << 1].add * tr[u].mul + tr[u].add) % mod;
tr[u << 1 | 1].add = ((LL) tr[u << 1 | 1].add * tr[u].mul + tr[u].add) % mod;
tr[u].add = 0;
tr[u].mul = 1;
}
void build(int u, int l, int r) {
tr[u] = {l, r};
tr[u].sum = 0;
tr[u].add = 0;
tr[u].mul = 1;
if(l == r) {
tr[u].sum = a[l] % mod;
} else {
int mid = l + (r - l) / 2;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify_add(int u, int l, int r, int d) {
if(l <= tr[u].l && tr[u].r <= r) {
tr[u].sum = (tr[u].sum + (LL) (tr[u].r - tr[u].l + 1) * d) % mod;
tr[u].add = (tr[u].add + d) % mod;
} else {
pushdown(u);
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
if(l <= mid) modify_add(u << 1, l, r, d);
if(r > mid) modify_add(u << 1 | 1, l, r, d);
pushup(u);
}
}
void modify_mul(int u, int l, int r, int v) {
if(l <= tr[u].l && tr[u].r <= r) {
tr[u].sum = tr[u].sum * v % mod;
tr[u].mul = tr[u].mul * v % mod;
tr[u].add = tr[u].add * v % mod;
}else {
pushdown(u);
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
if(l <= mid) modify_mul(u << 1, l, r, v);
if(r > mid) modify_mul(u << 1 | 1, l, r, v);
pushup(u);
}
}
LL query(int u, int l, int r) {
if(l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
else {
pushdown(u);
int mid = tr[u].l + (tr[u].r - tr[u].l) / 2;
LL sum = 0;
if(l <= mid) sum = query(u << 1, l, r) % mod;
if(r > mid) sum = (sum + query(u << 1 | 1, l, r)) % mod;
return sum;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> tt >> mod;
for(int i = 1; i <= n; i ++) cin >> a[i];
build(1, 1, n);
while(tt --) {
int op, l, r, k;
cin >> op;
if(op == 1) {
cin >> l >> r >> k;
modify_mul(1, l, r, k);
} else if(op == 2) {
cin >> l >> r >> k;
modify_add(1, l, r, k);
} else {
cin >> l >> r;
cout << query(1, l, r) << '\n';
}
}
return 0;
}