P8218 【深进1.例1】求区间和 杀鸡焉用宰牛刀

解题思路

这道题目要求我们计算多个区间的和,属于典型的区间查询问题。最直观的解法是对于每个查询直接遍历区间内的元素求和,但这种方法的时间复杂度为O(mn),当n和m较大时(如达到1e5)会超时。

更高效的解法是使用前缀和或线段树:

  1. 前缀和方法:预处理一个前缀和数组,可以在O(1)时间内回答每个区间查询。

  2. 线段树方法:构建一个线段树,每个节点存储对应区间的和,可以在O(logn)时间内回答每个区间查询。

虽然前缀和方法更简单高效,但题目提供的代码使用了线段树实现。线段树虽然代码量较大,但它是解决更复杂区间问题(如区间修改、区间最值等)的基础结构。

#include<bits/stdc++.h>
#define ll long long
#define lc rt << 1      // 左子节点的索引
#define rc rt << 1 | 1  // 右子节点的索引
#define lson lc,l,mid   // 左子节点的参数
#define rson rc,mid + 1,r // 右子节点的参数
using namespace std;

const int N = 2e5 + 10, inf = 0x3f3f3f3f;

// 线段树节点结构体,存储区间和
struct node{
    ll sum;
};

node t[N << 2];  // 线段树数组,大小是原数组的4倍
ll n, m;
ll 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) >> 1;  // 计算中点
    build(lson);  // 递归构建左子树
    build(rson);  // 递归构建右子树
    pushup(rt);   // 更新当前节点的值
}

// 区间查询函数
ll query(int rt, int l, int r, int x, int y)
{
    if(r < x || y < l) return 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;  // 读取序列长度
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);  // 读取序列数据
    build(1, 1, n);  // 构建线段树
    
    cin >> m;  // 读取查询数量
    while(m--)
    {
        int x, y; 
        scanf("%d%d", &x, &y);  // 读取查询区间
        printf("%lld\n", query(1, 1, n, x, y));  // 输出区间和
    }
    return 0;
}

 

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