给定一个数组 A和一些查询 Li,Ri,求数组中第 Li 至第 Ri个元素之和。
小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能地大。
小蓝想知道相比原数组,所有查询结果的总和最多可以增加多少?
输入格式
输入第一行包含一个整数 n。
第二行包含 n 个整数 A1,A2,⋅⋅⋅,An,相邻两个整数之间用一个空格分隔。
第三行包含一个整数 m表示查询的数目。
接下来 m 行,每行包含两个整数 Li、Ri,相邻两个整数之间用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
数据范围
对于 30%30% 的评测用例,n,m≤50;
对于 50%50% 的评测用例,n,m≤500
对于 70%70% 的评测用例,n,m≤5000
对于所有评测用例,1≤n,m≤1e5,1≤Ai≤1e6,1≤Li≤Ri≤n。
输入样例:
5
1 2 3 4 5
2
1 3
2 5
输出样例:
4
样例解释
原来的和为 6+14=206+14=20,重新排列为 (1,4,5,2,3)(1,4,5,2,3) 后和为 10+14=2410+14=24,增加了 44。
分析:
(1)选中每个不同的区域求和,我们要把每个区域出现的次数做个统计,如1~3,2~5,这两个区域2,3区域的数有两次相加出现两次,其他只有一次,所以可以用差分算法先统计一下每个区域出现的次数,想要结果和最大,就要将最大的数放在累加次数最高的位置
(2)由前缀和,将差分变成每个位置出现次数的数组
(3)和=【数字 * 出现次数】累加,可以去自己简单证明一下
(4)将两个数组排序,在分别用上式就可出最大值
#include<iostream>
#include<algorithm>
#define N 100010
typedef long long LL;
using namespace std;int a[N],diff[N];
int main(){int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}int m,l,r;cin>>m;for(int i=1;i<=m;i++){cin>>l>>r;diff[l]++,diff[r+1]--;//这是差分数组,变成原数组的意思就是在[l,r]区间+1}for(int i=1;i<=n;i++){diff[i]+=diff[i-1];//通过前缀和,变成每个位置出现次数的数组}int sum1=0;int sum2=0;for(int i=1;i<=n;i++){sum1+=(LL)(a[i]*diff[i]);}sort(a+1,a+n+1);sort(diff+1,diff+n+1);for(int i=1;i<=n;i++){sum2+=(LL)(a[i]*diff[i]);//cout<<sum2;}cout<<sum2-sum1;return 0;
}