【2024GXOI进阶】演唱会(live)

解题思路

这道题目需要处理两种操作:

  1. 查询操作:给定区间[l,r],计算该区间内所有粉丝应援力度的和乘以区间内的最大应援力度。

  2. 修改操作:将某个粉丝的应援力度修改为指定值。

由于数据规模较大(n和m最多为1e5),直接暴力计算每次查询会超时。因此需要使用线段树这种高效的数据结构来维护区间信息和快速查询。

线段树设计

线段树的每个节点需要维护两个信息:

  1. sum:区间内所有应援力度的和

  2. maxx:区间内的最大应援力度

这样对于每次查询操作,我们可以:

  1. 查询区间的和

  2. 查询区间的最大值

  3. 将两者相乘得到结果

对于修改操作,只需要更新对应位置的应援力度,并维护线段树的相关信息。

#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 = 1e5 + 10;  // 最大数据范围

// 线段树节点结构体
struct node{
    ll sum;    // 区间和
    ll maxx;   // 区间最大值
};

node t[N << 2]; // 线段树数组(大小为4N)
int n, m, a[N]; // n:粉丝数,m:操作数,a:应援力度数组

// 更新父节点信息
void pushup(int rt) {
    t[rt].sum = t[lc].sum + t[rc].sum;  // 区间和等于左右子区间和相加
    t[rt].maxx = max(t[lc].maxx, t[rc].maxx); // 区间最大值取左右子区间最大值
}

// 构建线段树
void build(int rt, int l, int r) {
    if(l == r) {  // 叶子节点
        t[rt].sum = t[rt].maxx = 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) {
    if(r < x || l > x) return;  // 超出修改范围则返回
    if(l == r) {  // 找到要修改的叶子节点
        t[rt].sum = y;   // 更新和
        t[rt].maxx = y;  // 更新最大值
        return;
    }
    int mid = (l + r) >> 1;
    change(lson, x, y);  // 修改左子树
    change(rson, x, y);  // 修改右子树
    pushup(rt);          // 更新当前节点信息
}

// 查询区间最大值
ll query1(int rt, int l, int r, int x, int y) {
    if(r < x || l > y) return 0;  // 超出查询范围返回0(不影响最大值比较)
    if(x <= l && r <= y) return t[rt].maxx;  // 完全包含则返回当前区间最大值
    int mid = (l + r) >> 1;
    // 返回左右子区间的最大值
    return max(query1(lson, x, y), query1(rson, x, y));
}

// 查询区间和
ll query2(int rt, int l, int r, int x, int y) {
    if(r < x || l > y) return 0;  // 超出查询范围返回0
    if(x <= l && r <= y) return t[rt].sum;  // 完全包含则返回当前区间和
    int mid = (l + r) >> 1;
    // 返回左右子区间的和
    return query2(lson, x, y) + query2(rson, x, y);
}

int main() {
    cin >> n >> m;  // 输入粉丝数和操作数
    for(int i = 1; i <= n; i++) cin >> a[i];  // 输入应援力度
    build(1, 1, n);  // 构建线段树
    
    while(m--) {
        int op, x, y;
        cin >> op >> x >> y;  // 输入操作类型和参数
        
        if(op == 1) {  // 查询操作
            ll maxx = query1(1, 1, n, x, y);  // 查询区间最大值
            ll sum = query2(1, 1, n, x, y);   // 查询区间和
            printf("%lld\n", maxx * sum);    // 输出结果
        }
        else {  // 修改操作
            change(1, 1, n, x, y);  // 修改指定位置的应援力度
        }
    }
    return 0;
}

复杂度分析

  • 构建线段树:O(n)

  • 修改操作:O(log n)

  • 查询操作:O(log n)(需要两次查询)

总复杂度:O(m log n),可以高效处理1e5规模的数据。

posted @ 2025-05-08 21:36  CRt0729  阅读(17)  评论(0)    收藏  举报