P2357 守墓人
守墓人问题 - 解题思路与代码注释
解题思路
这道题目需要使用线段树来高效处理区间更新和查询操作。线段树能够在O(logN)时间内完成区间增减和区间求和操作,非常适合处理大规模数据。
主要操作包括:
-
区间增减(操作1)
-
主墓碑单独增减(操作2、3)
-
区间查询(操作4)
-
主墓碑查询(操作5)
由于墓碑数量可能非常大(2×10^5),线段树是最合适的数据结构。主墓碑(1号)可以看作是线段树中最左边的叶子节点。
代码注释
#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 // 定义长整型别名 using namespace std; const int N = 2e5 + 10, inf = 0x3f3f3f3f; // 线段树节点结构体 struct node{ ll sum; // 区间和 ll lazy; // 懒惰标记,用于延迟更新 }; node t[N << 2]; // 线段树数组,大小是4倍原始数据量 ll n, m, a[N]; // n-墓碑数,m-操作数,a-初始风水值数组 // 向上更新节点信息 void pushup(int rt) { t[rt].sum = t[lc].sum + t[rc].sum; // 当前节点的和等于左右子节点和相加 } // 向下更新节点信息 void down(int rt, int l, int r, ll z) { t[rt].sum += (r - l + 1) * z; // 更新区间和 t[rt].lazy += z; // 更新懒惰标记 } // 向下传递懒惰标记 void pushdown(int rt, int l, int r) { if(t[rt].lazy == 0) return; // 如果没有懒惰标记,直接返回 int mid = (l + r) >> 1; down(lson, t[rt].lazy); // 更新左子树 down(rson, t[rt].lazy); // 更新右子树 t[rt].lazy = 0; // 清空当前节点的懒惰标记 } // 构建线段树 void build(int rt, int l, int r) { t[rt].lazy = 0; // 初始化懒惰标记 if(l == r){ // 如果是叶子节点 t[rt].sum = a[l]; // 直接赋值 return; } int mid = (l + r) >> 1; build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新当前节点信息 } // 区间更新操作 void change(int rt, int l, int r, int x, int y, ll z) { if(r < x || y < l) return; // 完全不在区间内,直接返回 if(x <= l && r <= y) // 当前区间完全在更新区间内 { t[rt].sum += (r - l + 1) * z; // 更新区间和 t[rt].lazy += z; // 设置懒惰标记 return; } pushdown(rt, l, r); // 向下传递懒惰标记 int mid = (l + r) >> 1; 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 0; // 完全不在查询区间内,返回0 if(x <= l && r <= y) return t[rt].sum; // 当前区间完全在查询区间内,直接返回和 pushdown(rt, l, r); // 向下传递懒惰标记 int mid = (l + r) >> 1; return query(lson, x, y) + query(rson, x, y); // 返回左右子树查询结果的和 } int main() { cin >> n >> m; for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); // 读取初始风水值 build(1, 1, n); // 构建线段树 while(m--) { int op; scanf("%d", &op); // 读取操作类型 if(op == 1){ // 区间增加操作 ll x, y, z; scanf("%lld%lld%lld", &x, &y, &z); change(1, 1, n, x, y, z); } if(op == 2){ // 主墓碑增加操作 ll z; scanf("%lld", &z); change(1, 1, n, 1, 1, z); } if(op == 3){ // 主墓碑减少操作 ll z; scanf("%lld", &z); change(1, 1, n, 1, 1, -z); } if(op == 4){ // 区间查询操作 ll x, y; scanf("%lld%lld", &x, &y); printf("%lld\n", query(1, 1, n, x, y)); } if(op == 5){ // 主墓碑查询操作 printf("%lld\n", query(1, 1, n, 1, 1)); } } return 0; }

浙公网安备 33010602011771号