P3374 【模板】树状数组 1 线段树解法
解题思路
这道题目要求实现一个数据结构,支持两种操作:
-
单点更新:将某个位置的数加上一个值
-
区间查询:查询某个区间内所有数的和
这是一个典型的动态区间求和问题,可以使用多种数据结构来解决。题目提供的代码使用了线段树解法。
方法选择
题目提供的线段树解法具有以下特点:
-
预处理时间:O(n)构建线段树
-
单点更新:O(logn)时间
-
区间查询:O(logn)时间
-
空间复杂度:O(n)
对于n和m都是5e5的数据规模,线段树的O(n + mlogn)复杂度完全能够胜任。
其他可能的解法
-
树状数组(Binary Indexed Tree):
-
代码更简洁
-
单点更新和区间查询都是O(logn)
-
空间复杂度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; // 存储区间和 }; 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 build(int rt, int l, int r) { if(l == r) { // 叶子节点 t[rt].sum = a[l]; // 存储单个元素值 return; } int mid = (l + r) / 2; // 计算中点 build(lson); // 构建左子树 build(rson); // 构建右子树 pushup(rt); // 更新当前节点的区间和 } // 单点更新函数(将x位置的值增加y) void change(int rt, int l, int r, int x, int y) { if(r < x || x < l) return; // 超出修改范围 if(l == r){ // 找到目标位置 t[rt].sum += y; // 增加y值 return; } int mid = (l + r) / 2; change(lson, x, y); // 更新左子树 change(rson, x, y); // 更新右子树 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; // 完全包含直接返回区间和 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; // 操作类型和参数 scanf("%d%d%d", &op, &x, &y); // 读取操作 if(op == 2){ // 查询操作 printf("%lld\n", query(1, 1, n, x, y)); } else if(op == 1) // 更新操作 { change(1, 1, n, x, y); // 执行单点更新 } } return 0; }

浙公网安备 33010602011771号