视频讲解
样例解析
我刚开始想着要想区间和最大排个降序即可,但是这从大到小排实际上不是最优策略。
我们来验证一下,就拿样例来验证。
1 2 3 4 5,原数组[1,3],[2,5]这段区间和是20。
排完序之后是5,4,3,2,1。[1,3],[2,5]这段区间的和是22。
从大到小排序后的数组的区间和比原数组的区间和大2。
我们再给出一种排列:1,4,5,2,3,区间和为24,比原数组区间和大4。
因此,从大到小排一定不是最优策略。
我们这里直接给出一个最优策略:
出现次数较多的*较大值,得出来的总值就较大。
在初始状态1,2,3,4,5中, 2,3在区间[1,3]中出现了一次,在区间[2,5]中出现了一次。
排序后变为1,4,5,2,3后,重复出现的变为了4,5。
于是问题就变为了怎么统计出每个数出现的次数。
n,m都是1e5,我们需要遍历m次[l,r]区间的查询,最后遍历完n,统计每个数出现的次数,复杂度是,光到这里就超时了。
因此我们这样要优化一下,可以用差分数组来统计每个数出现的次数。刚开始所有数都初始化为1,表示都出现了一次。然后对于m个查询,每个查询都对[l,r]区间内的值+1,这样就统计出来了每个数出现的次数。
注意,差分数组可以在O(1)时间内某个区间都加上c,刚开始每个数是0:0,0,0,0,然后[1,3]区间内每个数又出现了一次,应该是1,1,1,0,0。但是差分数组是单点修改,只需要给两段加上1即可,应该是:1,0,1,0,0。然后我们再用一个前缀和恢复一下,变为1,1,1,0,0。
code
a[i]最大1e6,有相加部分,记得开long long
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int N = 1e5 + 10;
int a[N], cnt[N];
bool cmp(int x,int y)
{
return x > y;
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
cin >> m;
while(m--)
{
int l, r; cin >>l>>r;
cnt[l] ++, cnt[r + 1] --;
}
//对差分数组还原,得到的就是每个数的出现次数
for (int i = 1; i <= n; i++)
{
cnt[i] =cnt[i]+cnt[i - 1];
}
//统计一下刚开始的区间和
int sum1=0;
for (int i = 1; i <= n; i++)
{
sum1 += a[i] * cnt[i];
}
//排个序,让出现次数多的 和 值较大的 排前面
sort(a + 1, a + n + 1,cmp);
sort(cnt+1,cnt+n+1,cmp);
//排完序的区间和
int sum2 = 0;
for (int i = 1; i <= n; i++)
{
sum2 += a[i] * cnt[i];
}
cout << sum2 - sum1 << endl;
return 0;
}