P3368 【模板】树状数组 2 线段树实现
解题思路
这道题目要求实现一个数据结构,支持两种操作:
-
区间更新:将区间[x,y]内的每个数加上一个值k
-
单点查询:查询某个位置x的值
这是一个典型的区间更新与单点查询问题。题目提供的代码使用了带有懒标记的线段树解法。
方法选择
线段树解法具有以下特点:
-
预处理时间:O(n)构建线段树
-
区间更新:O(logn)时间(使用懒标记)
-
单点查询:O(logn)时间
-
空间复杂度:O(n)
对于n和m都是5e5的数据规模,线段树的O(n + mlogn)复杂度完全能够胜任。
其他可能的解法
-
差分数组:
-
区间更新O(1)
-
单点查询O(n)(可通过前缀和优化为O(1))
-
空间复杂度O(n)
-
是本题的更优选择(题目名称也提示了这点)
-
-
树状数组:
-
可以实现区间更新和单点查询
-
但实现起来不如差分数组直观
-
代码注释
#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 = 2e6 + 10, inf = 0x3f3f3f3f; // 线段树节点结构体 struct node{ ll sum; // 存储区间和 ll lazy; // 懒标记,用于延迟更新 }; node t[N << 2]; // 线段树数组,大小是原数组的4倍 int n, m; // n-数列长度,m-操作数量 int a[N]; // 原始数列 // 更新父节点的区间和 void pushup(int rt) { t[rt].sum = t[lc].sum + t[rc].sum; // 父节点和等于左右子节点和相加 } // 下放懒标记 void down(int rt,int l,int r,ll num) { t[rt].sum += (r - l + 1) * num; // 更新区间和 t[rt].lazy += num; // 累加懒标记 } // 下推懒标记到子节点 void pushdown(int rt,int l,int r) { if(t[rt].lazy == 0) return; // 没有懒标记则返回 int mid = (l + r) / 2; 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) / 2; // 计算中点 build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新当前节点的区间和 } // 区间更新函数(将[x,y]区间的值增加z) 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].sum += (r - l + 1) * z; // 更新区间和 t[rt].lazy += z; // 设置懒标记 return; } pushdown(rt,l,r); // 下放懒标记 int mid = (l + r) / 2; change(lson, x, y,z); // 更新左子树 change(rson, x, y,z); // 更新右子树 pushup(rt); // 更新父节点的区间和 } // 区间查询函数(查询[x,y]区间和) 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) / 2; // 返回左右子树查询结果的和 return query(lson,x,y) + query(rson,x,y); } int main() { cin >> n >> m; // 读取数列长度和操作数量 for(int i = 1; i <= n; i++) scanf("%d", &a[i]); // 读取初始数列 build(1, 1, n); // 构建线段树 while(m--) // 处理每个操作 { int op, x, y, z; // 操作类型和参数 scanf("%d%d", &op, &x); // 读取操作 if(op == 2){ // 查询操作(单点查询) printf("%lld\n", query(1, 1, n, x, x)); } else if(op == 1) // 更新操作(区间更新) { scanf("%d%d",&y,&z); change(1, 1, n, x, y, z); // 执行区间更新 } } return 0; }

浙公网安备 33010602011771号