上海计算机学会2025年2月月赛C++丙组T3区间求和

区间求和

内存限制: 512 Mb时间限制: 1000 ms

题目描述

Carol 最近正在学习前缀和:给定序列 a1∼n​ 与 q 次询问,每次询问给出 l,r,求 al∼r​ 的和。这样的问题对 Carol 来说已经太简单了,他发现交换一些序列 a 中的元素的值可以改变询问的结果。

面对这个发现,他提出了一个新的问题:给定序列 a1∼n​ 与 q 次询问,每次询问给出 l,r,如果可以在执行询问前任意次交换 a 中两个元素的位置得到 a1∼n′​,所有询问的答案之和最大是多少?一次询问的答案指的是 al∼r′​ 的和。

需要注意的是,Carol 只能在开始询问前交换元素,然后求出所有询问的答案之和。

输入格式

第一行一个整数 T 表示数据组数,对于每组数据:

第一行两个整数 n,q。

第二行 n 个整数 a1∼n​。

接下来 q 行,第 i 行两个整数 li​,ri​ 表示第 i 次询问的参数。

输出格式

对于每组数据,输出一行一个答案表示最大的询问答案之和。

数据范围

对于 30% 的数据,1≤T≤10,1≤n,q≤10。

对于 60% 的数据,1≤T≤10,1≤n,q≤1000。

对于 100% 的数据,1≤T≤10^4,1≤n,∑n≤2×10^5,1≤q,∑q≤2×10^5,1≤ai≤10^5,1≤l≤r≤n。

样例数据

 输入:
2
5 2
1 2 3 4 5
1 4
2 3
2 3
1 1
1 1
1 2
2 2
输出:
23
4
说明:
样例解释:对于第一组数据,把a变为2 4 5 3 1即可得到答案之和为23,可以验证没有更大的答案之和。

解析:可以先将q个区间利用差分,求出每个位置在和中的使用次数,然后按从大到小的顺序将使用次数排序,然后将n个数从大到小排序,利用贪心策略,让最大的数使用次数最多,即为答案,详见代码:

#include <bits/stdc++.h>
using namespace std;
long long a[200005];
int b[200005];
int main() {
    int t, n, q;
    cin >> t;
    while(t--) {
        cin >> n >> q;
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            b[i] = 0;//差分数组归零
        }
        for(int i = 1; i <= q; i++) {//将q个区间差分
            int l, r;
            cin >> l >> r;
            b[l]++;
            b[r + 1]--;
        }
        for(int i = 1; i <= n; i++) {//前缀和,求出每个位置的使用次数
            b[i] += b[i - 1];
        }
        sort(a + 1, a + n + 1, greater<int>());//数字从大到小排序
        sort(b + 1, b + n + 1, greater<int>());//使用次数从大到小排序
        long long ans = 0;//注意数据范围
        for(int i = 1; i <= n; i++) {//贪心策略
            if (b[i] == 0) break;//如果后续使用次数为0,结束计算
            ans += a[i] * b[i];//使用次数最多的用最大的数
        }
        cout << ans << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长春高老师信奥工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值