题意:
给一个数组,和一些查询的区间,你要将这些区间和求出来设位s1,你现在要重新对这个数组进行排序,同样求出这些区间的和设为s2求出s2-s1的最大值。
思路:
首先我们要清楚前缀和,也就是这个区间的加法,当越大的那部分值被加的次数越多,那么我们的s2就会越大。
我们可以看出红色区域是出现次数最多的,也就意味着我们这一部分的值越大我们所求出的s2也将会越大。所以我们转过来就可以想哪些位置用的次数越多我们就应该给他赋值就是越大的。
那现在我们就要考虑怎么求出哪些位置用了多少次呢?
因为询问的是一段连续区间,这儿就可以联想到我们的差分了。
我们定义一个数组为b,我们也称之为差分数组,当我们的
l
=
2
,
r
=
7
l = 2, r = 7
l=2,r=7的时候意味着我们2~7这个区间的数都会被用到一次那么我们就设b[2] = 1, b[8] = -1,其他默认为0。
那么此时我们再去求一遍b数组的前缀和就为[0, 1, 1, 1, 1, 1, 1, 0, 0],可以看出2~7之间就都变为1了。
现在还有一个区间[4, 7];
我们就让b[4]++, b[8]–我们再求b的前缀和就为[0, 1, 1, 2, 2, 2, 2, 0, 0]可以看出此时4~7的值都为2了,就代表我们的这个区间用了两次。
总结一下对一个区间[l, r]进行差分那么就是b[l] += x, b[r+1]-=x; 至于为什么是r+1可以再细看一下上述这个图求一下前缀和就能理解了。
然后我们把差分数组求一遍前缀和,我们把前缀和越大的值就赋值原数组中值越大的然后再对新数组对应区间求一下前缀和就是了。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define int long long
#define endl "\n"
using namespace std;
const int N = 2e5 + 10;
int n, m, k, _;
int arr[N], sum[N], brr[N], crr[N];
struct node
{
int id, va;
}e[N];//id代表我们到时候插入新的值得时候好对应
struct edge
{
int l, r;
}q[N];
bool cmp(node a, node b)
{
return a.va > b.va;
}
signed main()
{
IOS;
cin >> n;
for (int i = 1; i <= n; i++) cin >> arr[i], sum[i] = sum[i-1] + arr[i];//原数组求前缀和
cin >> m;
sort(arr + 1, arr + 1 + n);//记得排序
reverse(arr + 1, arr + 1 + n);//反转方便我们后面找值
int s1 = 0, s2 = 0;
for(int i = 1; i <= m; i ++)
{
int l, r;
cin >> l >> r;
q[i].l = l, q[i].r = r;//保存下来后面求得新数组再求区间和
s1 += sum[r] - sum[l - 1];//原数组的区间和
brr[l] += 1;//差分数组
brr[r + 1] -= 1;
}
for (int i = 1; i <= n; i++)
{
crr[i] = crr[i - 1] + brr[i];
e[i].id = i, e[i].va = crr[i];
}//求前缀和
sort(e + 1, e + 1 + n, cmp);//排序顺便记录下下标
for (int i = 1; i <= n; i++) brr[e[i].id] = arr[i];//值越大得赋值也该越大
for (int i = 1; i <= n; i++) brr[i] += brr[i-1];//新数组得前缀和
for (int i = 1; i <= m; i++) s2 += brr[q[i].r] - brr[q[i].l - 1];
cout << s2 - s1 << endl;
return 0;
}