P1253 扶苏的问题
扶苏的问题 - 线段树解法
解题思路
这道题需要处理三种区间操作:区间赋值、区间加法、区间最大值查询。线段树是解决这类区间操作问题的理想数据结构。
关键点分析:
-
双标记处理:同时存在赋值和加法两种操作,需要设计合理的标记处理顺序
-
标记优先级:赋值操作会覆盖之前的加法标记,但加法操作不会影响赋值标记
-
高效查询:需要维护区间最大值信息,支持快速查询
解决策略:
-
使用线段树节点存储区间最大值、加法标记和赋值标记
-
赋值操作时,清空加法标记
-
下推标记时,先处理赋值标记再处理加法标记
-
每次操作后维护区间最大值信息
代码注释
#include<bits/stdc++.h> #define lc rt << 1 // 左子节点索引 #define rc rt << 1 | 1 // 右子节点索引 #define lson lc,l,mid // 左子树参数 #define rson rc,mid + 1,r // 右子树参数 #define ll long long // 定义long long类型别名 using namespace std; const ll N = 1e6 + 10, inf = 1e18; // 线段树节点结构体 struct node { ll maxx, k, lazy; // maxx:区间最大值,k:赋值标记,lazy:加法标记 }; node t[N * 4]; // 线段树数组,4倍空间 ll n, m, a[N]; // n:序列长度,m:操作次数,a:原始数组 // 下推标记函数 void pushdown(int rt, int l, int r) { // 如果没有标记则直接返回 if(t[rt].lazy == 0 && t[rt].k == -inf) return; // 优先处理赋值标记(优先级高于加法标记) if(t[rt].k != -inf) { t[lc].maxx = t[rc].maxx = t[rt].k; // 更新左右子树最大值 t[lc].lazy = t[rc].lazy = 0; // 清空左右子树的加法标记 t[lc].k = t[rc].k = t[rt].k; // 设置左右子树的赋值标记 t[rt].k = -inf; // 清空当前节点的赋值标记 } // 处理加法标记 if(t[rt].lazy != 0) { t[lc].maxx += t[rt].lazy; // 左子树最大值增加 t[rc].maxx += t[rt].lazy; // 右子树最大值增加 t[lc].lazy += t[rt].lazy; // 左子树加法标记累加 t[rc].lazy += t[rt].lazy; // 右子树加法标记累加 t[rt].lazy = 0; // 清空当前节点的加法标记 } } // 上推信息函数 void pushup(int rt) { // 用左右子树的最大值更新当前节点的最大值 t[rt].maxx = max(t[lc].maxx, t[rc].maxx); } // 构建线段树 void build(int rt, int l, int r) { t[rt].lazy = 0, t[rt].k = -inf; // 初始化标记 if(l == r) { // 叶子节点 t[rt].maxx = a[l]; // 存储原始数组值 return; } int mid = (l + r) / 2; build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新当前节点信息 } // 区间加法操作 void add(int rt, int l, int r, int x, int y, int z) { if(r < x || y < l) return; // 区间无交集 if(x <= l && r <= y) { // 当前区间完全包含在目标区间内 t[rt].maxx += z; // 更新区间最大值 t[rt].lazy += z; // 累加加法标记 return; } pushdown(rt, l, r); // 下推标记 int mid = (l + r) / 2; add(lson, x, y, z); // 更新左子树 add(rson, x, y, z); // 更新右子树 pushup(rt); // 更新当前节点信息 } // 区间赋值操作 void change(int rt, int l, int r, int x, int y, int z) { if(r < x || y < l) return; // 区间无交集 if(x <= l && r <= y) { // 当前区间完全包含在目标区间内 t[rt].maxx = z; // 更新区间最大值 t[rt].lazy = 0; // 清空加法标记(赋值操作优先级更高) t[rt].k = z; // 设置赋值标记 return; } pushdown(rt, l, r); // 下推标记 int mid = (l + r) / 2; change(lson, x, y, z); // 更新左子树 change(rson, x, y, z); // 更新右子树 pushup(rt); // 更新当前节点信息 } // 区间最大值查询 ll query(int rt, int l, int r, int x, int y) { if(r < x || y < l) return -inf; // 区间无交集,返回极小值 if(x <= l && r <= y) return t[rt].maxx; // 直接返回区间最大值 pushdown(rt, l, r); // 下推标记 int mid = (l + r) / 2; // 返回左右子树查询结果的较大值 return max(query(lson, x, y), query(rson, x, y)); } int main() { scanf("%lld%lld", &n, &m); // 读取序列长度和操作次数 for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); // 读取初始序列 build(1, 1, n); // 构建线段树 while(m--) { int op, x, y, z; scanf("%d", &op); // 读取操作类型 if(op == 1) { // 区间赋值操作 scanf("%d%d%d", &x, &y, &z); change(1, 1, n, x, y, z); } else if(op == 2) { // 区间加法操作 scanf("%d%d%d", &x, &y, &z); add(1, 1, n, x, y, z); } else if(op == 3) { // 区间最大值查询 scanf("%d%d", &x, &y); ll ans = query(1, 1, n, x, y); printf("%lld\n", ans); } } return 0; }

浙公网安备 33010602011771号