Acwing 4655重新排序

文章介绍了一种处理数组区间和问题的方法,通过使用前缀和和差分数组来优化计算过程。首先,理解区间和与前缀和的关系,然后利用差分数组记录每个区间被使用的次数。通过对差分数组求前缀和并进行排序,将最大的值赋予原数组中的大值,从而在重新计算区间和时得到最大差值。
摘要由CSDN通过智能技术生成

题意:

给一个数组,和一些查询的区间,你要将这些区间和求出来设位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;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值