Files
python/TangDou/AcWing/SegmentTree/P1253_XDS.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

173 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m;
int a[N];
// 线段树模板
#define int long long
#define INF 0x3f3f3f3f
#define ls u << 1
#define rs u << 1 | 1
struct Node {
int l, r;
int modify, add; // 两个懒标记:区间内统一修改为v,区间内统一加上v
int mx; // 区间最大值,结果
} tr[N << 2];
void pushup(int u) {
tr[u].mx = max(tr[ls].mx, tr[rs].mx);
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r; // 范围
tr[u].modify = INF; // 更新懒标记INF,更新懒标记INF
tr[u].add = 0; // 更新懒标记INF,加法懒标记0
/*
Q1:为什么modify的懒标记要设置为INF,而不是0呢?
答:因为如果设置为0,我们就不知道到底是想整体区间都修改为0,还是默认值!我们分不清!
同时,由于modify的值域是abs(x)=1e9,也就是可能是0,也可能是-1e9,所以,我们只能选择找一个范围外的数字做为初始值,
才能区分开是默认值,还是人为修改值。
Q2: 为什么add懒标记可以设置为初始化是0呢?
答:因为对于加法而言,加零还是不加零是没有区别的,所以,人为区间加,是不会加零的。
Q: 为什么这个对于懒标记的初始化,是在build这个递归函数的入口处就进行呢的?而对于叶子节点的赋值是在l==r的判断里?
答:我们需要头脑中有线段树的整体架构,在每个统计区间(也可以理解为每个统计点),都是有两个懒标记:整体修改标记modify
和整体加法标记add的,懒标记可不是作用到叶子上的,而是作用在统计节点上的!所以,对于每个需要分裂的区间,当然都需要对
懒标记进行赋值了。
*/
if (l == r) {
tr[u].mx = a[l]; // 单节点时,区间最大值,准备通过pushup函数,向上汇总统计信息
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(u);
}
void pushdown(int u) {
// 注意:要先处理统一修改的modify懒标记,再处理add标记!
if (tr[u].modify != INF) { // 如果存在modify的懒标记
tr[ls].modify = tr[rs].modify = tr[u].modify; // 向左右儿子传递统一修改的懒标记
tr[ls].mx = tr[rs].mx = tr[u].modify; // 统一修改后,都是一样的值,那么左右儿子的最大值当然也是这个值
tr[ls].add = tr[rs].add = 0; // 本来想向下传递加法的懒标记,这样好了,不用传递了,因为新来的要求把整体都修改成modify了
tr[u].modify = INF; // modify懒标记都传递下去,处理完了,设置为默认值吧
}
if (tr[u].add) { // 如果存在add的懒标记
tr[ls].add += tr[u].add; // 左儿子的add懒标记加上新的增加出来的add
tr[rs].add += tr[u].add; // 右儿子的add懒标记加上新的增加出来的add
tr[ls].mx += tr[u].add; // 左儿子的最大值加上add
tr[rs].mx += tr[u].add; // 右儿子的最大值加上add
tr[u].add = 0; // add懒标记传递完毕
}
}
void modify(int u, int l, int r, int v) {
if (l <= tr[u].l && r >= tr[u].r) { // 完全覆盖
tr[u].mx = v; // 区间所有值修改为v,当然最大值也是v
tr[u].modify = v; // 全部修改为v,懒标记修改为v, 不向下继续传递
tr[u].add = 0; // 整体都修改为v了,以前不管有啥add懒标记,其实都没用了,因为人家要统一修改为v
return;
}
pushdown(u); // 懒标记下传时,不一定是一个懒标记,所以,不要在这里通过if进行判断决定是否进行pushdown,而是在pushdown内部完成懒标记的判定
// 找交集
// 方法1
if (l <= tr[ls].r) modify(ls, l, r, v); // 左儿子的右大于等于l,表示左儿子与修改区间有交集
if (r >= tr[rs].l) modify(rs, l, r, v); // 右儿子的左小于等于r,表示右儿子与修改区间有交集
// 方法2
// int mid = (tr[u].l + tr[u].r) >> 1;
// if (l <= mid) modify(ls, l, r, v);
// if (r > mid) modify(rs, l, r, v);
// 修改完毕,需要向上汇总统计信息
pushup(u);
}
void add(int u, int l, int r, int v) { // 加v
if (l <= tr[u].l && r >= tr[u].r) { // 完全覆盖
tr[u].mx += v; // 每个人都+v,最大值肯定也要+v
tr[u].add += v; // 修改整体的加法懒标记
// Q:为什么不修改modify懒标记呢?
// 答:当add执行时,需要把前面的都整明白后再进行add。那么,如果原来有modify的动作,就先modify,再add, 否则计算就不正确了
return;
}
pushdown(u);
// 找交集
// 方法1
if (l <= tr[ls].r) add(ls, l, r, v); // 左儿子的右大于等于l,表示左儿子与修改区间有交集
if (r >= tr[rs].l) add(rs, l, r, v); // 右儿子的左小于等于r,表示右儿子与修改区间有交集
// 方法2
// int mid = (tr[u].l + tr[u].r) >> 1;
// if (l <= mid) add(ls, l, r, v);
// if (r > mid) add(rs, l, r, v);
// 修改完毕,需要向上汇总统计信息
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) return tr[u].mx;
pushdown(u);
// 方法1
if (r < tr[rs].l) return query(ls, l, r);
if (l > tr[ls].r) return query(rs, l, r);
return max(query(ls, l, r), query(rs, l, r));
// 方法2
// int mid = (tr[u].l + tr[u].r) >> 1;
// int res = -1e16;
// if (l <= mid) res = max(res, query(ls, l, r));
// if (r > mid) res = max(res, query(rs, l, r));
// return res;
}
/*
答案:
7
6
-1
*/
signed main() {
#ifndef ONLINE_JUDGE
freopen("P1253.in", "r", stdin);
#endif
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
while (m--) {
int op;
int l, r, x;
cin >> op;
if (op == 1) {
cin >> l >> r >> x;
modify(1, l, r, x);
} else if (op == 2) {
cin >> l >> r >> x;
add(1, l, r, x);
} else {
cin >> l >> r;
printf("%lld\n", query(1, l, r));
}
}
return 0;
}