• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

yongchaoD

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

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;
}

posted on 2024-07-17 20:57  yongchaoD  阅读(35)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3