【第十三届蓝桥杯 C/C++ 组】重新排序
给定一个数组 A A A 和一些查询 L i , R i L_i,R_i Li,Ri ,求数组中第 L i L_i Li 至第 R i R_i Ri 个元素之和。
小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能地大。
小蓝想知道相比原数组,所有查询结果的总和最多可以增加多少?
输入格式
输入第一行包含一个整数
n
n
n。
第二行包含 n n n 个整数 A 1 , A 2 , ⋅ ⋅ ⋅ , A n A_1,A_2,⋅⋅⋅,A_n A1,A2,⋅⋅⋅,An ,相邻两个整数之间用一个空格分隔。
第三行包含一个整数 m m m 表示查询的数目。
接下来 m m m 行,每行包含两个整数 L i L_i Li 、 R i R_i Ri,相邻两个整数之间用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
数据范围
对于 30% 的评测用例,
n
,
m
≤
50
n,m≤50
n,m≤50;
对于 50% 的评测用例,
n
,
m
≤
500
n,m≤500
n,m≤500;
对于 70% 的评测用例,
n
,
m
≤
5000
n,m≤5000
n,m≤5000;
对于所有评测用例,
1
≤
n
,
m
≤
105
1≤n,m≤105
1≤n,m≤105,
1
≤
A
i
≤
106
1≤Ai≤106
1≤Ai≤106,
1
≤
L
i
≤
R
i
≤
n
1≤Li≤Ri≤n
1≤Li≤Ri≤n。
输入样例:
5
1 2 3 4 5
2
1 3
2 5
输出样例:
4
样例解释
原来的和为
6
+
14
=
20
6+14=20
6+14=20 ,重新排列为
(
1
,
4
,
5
,
2
,
3
)
(1,4,5,2,3)
(1,4,5,2,3) 后和为
10
+
14
=
24
10+14=24
10+14=24 ,增加了
4
4
4。
思路
粗看本题似乎不难,但是如果模拟排序过程就会发现太复杂。
通过观察我们可以发现实际上就是让原数组中最大的值排序到给出的所有区间里交集次数最多的地方,这样就能得到符合题意的数组。
但是若模拟排序过程会非常复杂并且会超时,因此我们可以建立两个数组,一个按序储存原数组,另外一个按序储存交集次数下标,最后相乘求和。
这里要用到排序不等式原理:大数乘大数加上小数乘小数,大于大数乘小数加小数乘大数。这里简单证明一下:
设· A i > A i + 1 , C i < C i + 1 A_i>A_ {i+1},C_i<C_{i+1} Ai>Ai+1,Ci<Ci+1, 那么有
( A i C i + A i + 1 C i + 1 ) − ( A i C i + 1 + A i + 1 C i ) (A_i C_i + A_ {i+1} C_{i+1})-(A_i C_{i+1} + A_ {i+1} C_i) (AiCi+Ai+1Ci+1)−(AiCi+1+Ai+1Ci) = ( A i − A i + 1 ) ( C i − C i + 1 ) < 0 =(A_i-A_ {i+1})(C_i-C_{i+1})<0 =(Ai−Ai+1)(Ci−Ci+1)<0
即证 A i C i + 1 > A i + 1 C i A_i C_{i+1}>A_ {i+1}C_i AiCi+1>Ai+1Ci。
此外,还需要使用差分数组维护区间进行区间累加。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
int n, m;
int w[N],s[N];//w为原数组,s为差分数组
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
cin>>m;
while(m--){
int l,r;
cin>>l>>r;
s[l]++; s[r+1]--;//建立差分数组
}
for(int i=1;i<=n;i++)
s[i]+=s[i-1]; //差分数组还原为原数组
LL sum1=0;
for(int i=1;i<=n;i++)
sum1+=w[i]*s[i];//原数组最大值
sort(w+1,w+n+1);
sort(s+1,s+n+1);
LL sum2=0;
for(int i=1;i<=n;i++)
sum2+=w[i]*s[i];//排序后最大值
cout<<sum2-sum1<<endl;
return 0;
}