P3374 【模板】树状数组 1 线段树解法

解题思路

这道题目要求实现一个数据结构,支持两种操作:

  1. 单点更新:将某个位置的数加上一个值

  2. 区间查询:查询某个区间内所有数的和

这是一个典型的动态区间求和问题,可以使用多种数据结构来解决。题目提供的代码使用了线段树解法。

方法选择

题目提供的线段树解法具有以下特点:

  1. 预处理时间:O(n)构建线段树

  2. 单点更新:O(logn)时间

  3. 区间查询:O(logn)时间

  4. 空间复杂度:O(n)

对于n和m都是5e5的数据规模,线段树的O(n + mlogn)复杂度完全能够胜任。

其他可能的解法

  1. 树状数组(Binary Indexed Tree)

    • 代码更简洁

    • 单点更新和区间查询都是O(logn)

    • 空间复杂度O(n)

    • 是本题的更优选择(题目名称也提示了这点)

  2. 分块处理

    • 将数组分成若干块,预处理每块的和

    • 单点更新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;
}

 

posted @ 2025-05-21 09:47  CRt0729  阅读(33)  评论(0)    收藏  举报