解题思路
Part 1:贪心
我们可以累计每个 AiAi 的被求和次数 cici。容易贪心得到,被求和次数越多的肯定得放越大的数(可用邻项交换证明)(排序不等式)。
我们可以先统计原来的求和的总和 sumsum,再给 AA 数组和统计求和次数的数组 cc 从小到大排好序,最后依次相乘起来即 ∑i=1naici∑i=1naici 减去原来的总和 sumsum 便是答案。
Part 2:差分
统计求和次数时注意到只有修改操作而没有查询操作,于是可以用差分来维护。
具体地,我们建立一个都为 00 的差分数组 bb,当 l∼rl∼r 这段区间被求和时,就 将 blbl 加上 11,br+1br+1 减去 11 就可以了。然后在求前缀和
注意:原来的总和和最大总和都要用 long long 来存储。
时间复杂度:Θ(nlogn)Θ(nlogn)。
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int a[N],b[N];
int n,m;
//差分 既可以构造差分又可以在一段区间上加数
void insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
scanf("%d ",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
while(m--)
{
int l,r;scanf("%d%d",&l,&r);
insert(l,r,1);
}
//求前缀和,统计a数组中 每个数要加多少次
for(int i=1;i<=n;i++) b[i]+=b[i-1];
ll sum1=0,sum2=0;
//没有移动之前的求和
for(int i=1;i<=n;i++) sum1+=(ll)a[i]*b[i];
//要想两数差最大,则让后者最大,让出现次数最多的与数最大的相匹配
sort(a+1,a+1+n);
sort(b+1,b+1+n);
for(int i=1;i<=n;i++) sum2+=(ll)a[i]*b[i];
cout<<sum2-sum1<<endl;
return 0;
}