P1253 扶苏的问题(线段树:区间修改,最值,改定值)
题目来源:https://www.luogu.com.cn/problem/P1253
//
教程来源(b站):【信息学奥赛C++提高组-06课-3-线段树- P1253 扶苏的问题-线段树】https://www.bilibili.com/video/BV1Hm411U7bV?vd_source=edd8d483423d58308aefa72fbec9bd22
//
题意:3种操作:op1:[l,r]内的每个数都修改为x。 op2:[l,r]内的每个数加x。op3:查询区间[l,r]内的最大值。
//
思路:这个线段树多了个把区间都改成定值,会多一个cover标记,用于(l <= s && r >= t) 打上cover标记。最核心的地方就是cover标记和add标记的先后顺序,如果p节点有cover标记,那么p节点之前所有的add标记都是作废!
//
代码细节:
1:tree[p]维护的是p区间范围的最大值。
2:在update和recover函数中,因为存在打add和cover标记,那么p节点的最值会改变,那么p的父节点tree[fa]肯定是要更新的,所以会有pushup:tree[p] = max(tree[lp(p)], tree[rp(p)]); 而query函数中是不需要pushup函数,查询是不需要更改值的,只是在有标记的时候,要pushdown下传,下传的过程中就会把tree[p]给更新了((tree[lp(p)]+=add[p])。tree[lp(p)] = tree[p]。)
//
“时间久了,光看思路回顾是很难体会到当时的感受和启发,如果要复盘这个线段树,建议仔细看题解的注释代码然后重敲一遍,这个b题我写了接近一天,调bug接近1h(pushdown中标记下传的地方容易晕)”
//
题解:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 9;
vector<int> arr(N), tree(4 * N, 0), add(4 * N, 0), cover(4 * N, 0);
int n, q, L, R, k, op;
int lp(int p) { return p << 1; }
int rp(int p) { return p << 1 | 1; }
void pushdown(int p) {//先是cover标记,再是add标记
if (cover[p] == 1) {//cover标记:最值tree[p]=k,add标记全清0(都全赋值了,之前的add也被覆盖了)
//tree[p]=k;重复了
add[p] = 0, cover[p] = 0;//标记清空
tree[lp(p)] = tree[p], tree[rp(p)] = tree[p];//更新新值:父节点被cover更新过了,那孩子节点直接等于tree[fa]就相当于被cover了
cover[lp(p)] = 1, cover[rp(p)] = 1;//cover标记下传
add[lp(p)] = 0, add[rp(p)] = 0;//孩子节点add标记清空
} else if (add[p] != 0) {//add标记
// tree[p] += k;
// tree[lp(p)] += k, tree[rp(p)] += k;
// add[p] = 0;
add[lp(p)]+=add[p],add[rp(p)]+=add[p];//add标记下传给孩子
tree[lp(p)]+=add[p],tree[rp(p)]+=add[p];
add[p]=0;
}
}
void build(int s, int t, int p) {
if (s == t) {
tree[p] = arr[s];
return;
}
int m = (s + t) >> 1;
build(s, m, lp(p)), build(m + 1, t, rp(p));
tree[p] = max(tree[lp(p)], tree[rp(p)]);//tree维护最大值
}
int query(int l, int r, int s, int t, int p) {
if (l <= s && r >= t) {
return tree[p];
}
pushdown(p);
int m = (s + t) >> 1, Min1 = -1e18, Min2 = -1e18;
if (l <= m) { Min1 = query(l, r, s, m, lp(p)); }
if (r >= m + 1) { Min2 = query(l, r, m + 1, t, rp(p)); }
return max(Min1, Min2);//query不需要pushup,pushdown中的add标记更新了tree[p]
}
void update(int l, int r, int c, int s, int t, int p) {
if (l <= s && r >= t) {//add标记
tree[p] += c, add[p] += c;//tree[p]+=c,而不知
return;
}
pushdown(p);
int m = (s + t) >> 1;
if (l <= m) { update(l, r, c, s, m, lp(p)); }
if (r >= m + 1) { update(l, r, c, m + 1, t, rp(p)); }
tree[p] = max(tree[lp(p)], tree[rp(p)]);//???可不可以不写
}
void recover(int l, int r, int c, int s, int t, int p) {
if (l <= s && r >= t) {//cover标记:区间最值=c
tree[p] = c, cover[p] = 1;//cover,tree[p]的区间最值就是c
add[p] = 0;
return;
}
pushdown(p);
int m = (s + t) >> 1;
if (l <= m) { recover(l, r, c, s, m, lp(p)); }
if (r >= m + 1) { recover(l, r, c, m + 1, t, rp(p)); }
tree[p] = max(tree[lp(p)], tree[rp(p)]);//要写
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> arr[i];
build(1, n, 1);
while (q--) {
cin >> op;
if (op == 1) {//[L,R]定值=k
cin >> L >> R >> k;
recover(L, R, k, 1, n, 1);
} else if (op == 2) {//[L,R]+=k
cin >> L >> R >> k;
update(L, R, k, 1, n, 1);
} else if (op == 3) {//查询[L,R]最大值
cin >> L >> R;
cout << query(L, R, 1, n, 1) << '\n';
}
}
return 0;
}
浙公网安备 33010602011771号