题目描述
给定一个数组 $A$ 和一些查询 $L _ i, R _ i$,求数组中第 $L _ i$ 至第 $R _ i$ 个元素之和。
小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能地大。
小蓝想知道相比原数组,所有查询结果的总和最多可以增加多少?
输入格式
输入第一行包含一个整数 $n$。
第二行包含 $n$ 个整数 $A _ 1, A _ 2, \ldots, A _ n$,相邻两个整数之间用一个空格分隔。
第三行包含一个整数 $m$ 表示查询的数目。
接下来 $m$ 行,每行包含两个整数 $L _ i$、$R _ i$,相邻两个整数之间用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
数据范围
对于 $30 \%$ 的评测用例,$n, m \le 50$;
对于 $50 \%$ 的评测用例,$n, m \le 500$;
对于 $70 \%$ 的评测用例,$n, m \le 5000$;
对于所有评测用例,$1 \le n, m \le 10 ^ 5$,$1 \le A _ i \le 10 ^ 6$,$1 \le L _ i \le R _ i \le n$。
输入样例:
5
1 2 3 4 5
2
1 3
2 5
输出样例:
4
样例解释
原来的和为 $6 + 14 = 20$,重新排列为 $(1, 4, 5, 2, 3)$ 后和为 $10 + 14 = 24$,增加了 $4$。
解题思路
Part 1:贪心
我们可以累计每个 $A _ i$ 的被求和次数 $c _ i$。容易贪心得到,被求和次数越多的肯定得放越大的数(可用邻项交换证明)。
我们可以先统计原来的求和的总和 $sum$,再给 $A$ 数组和统计求和次数的数组 $\mathbf {c}$ 从小到大排好序,最后依次相乘起来即 $\mathbf {\sum \limits _ {i = 1} ^ n a _ i c _ i}$ 减去原来的总和 $\mathbf {sum}$ 便是答案。
Part 2:差分
统计求和次数时注意到只有修改操作而没有查询操作,于是可以用差分来维护。
具体地,我们建立一个都为 $0$ 的差分数组 $b$,当 $l \sim r$ 这段区间被求和时,就 将 $\mathbf {b _ l}$ 加上 $\mathbf 1$,$\mathbf {b _ {r + 1}}$ 减去 $\mathbf 1$ 就可以了。
注意:原来的总和和最大总和都要用 long long
来存储。
时间复杂度:$\Theta (n \log n)$。
AC Code
#include <iostream>
#include <algorithm>
#define N 100005
using namespace std;
int n, m, l, r, a[N], b[N];
long long sum, ans;
int main ()
{
cin >> n;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
}
cin >> m;
while (m --)
{
cin >> l >> r, b[l] ++, b[r + 1] --; // 同时处理差分
}
for (int i = 1; i <= n; i ++)
{
b[i] += b[i - 1], sum += (long long) a[i] * b[i]; // 先得到数组 c(仍存放在数组 b 中),在顺便统计原来的总和 sum
}
sort (a + 1, a + n + 1), sort (b + 1, b + n + 1); // 分别排序
for (int i = 1; i <= n; i ++)
{
ans += (long long) a[i] * b[i]; // 加上依次相乘的和
}
cout << ans - sum; // 答案即是依次相乘总和 - sum
return 0;
}
感谢观看!
$$\href {/blog/content/29204/} {\color {fuchsia} {【寒假每日一题】题解}}$$
贴一个Python的
python版本的好清爽,好优雅!
为啥要翻转排序啊?
这个题要不是翻转排序的话就错了😂
差分真是遇到一次不会一次,麻了呀
差分其实就是把一段数加上同一个数化成了变化两个位置的差分数组的数的操作,本质是转化操作,让他更快
Orz
6
想知道为什么要求前缀和,差分后每个区间不是都加上相应需要处理的次数了吗
这里用差分的作用是方便求每一个Ai对应被加了多少次 (他这里结果还是放在了b数组里)。因为需要每一个数被加的次数 (添加数的总次数是不变的)。
感谢感谢,现在已经理解了,以前理解得比较浅显,惭愧~
long long 虐我千百遍
给a数组开了一个前缀和s, 处理m次询问的时候让sum += s[r] - s[l - 1];
莫名几秒有个点segment fault qwq
如果开的是int类型的前缀和数组 会爆吧 开
long long s[N]
不会出现错误的哥们开的是__int128的 呜呜呜
### 感谢分享
我又加了一个数组,三个区分开了。
还没学到贪心QWQ
差分用在统计被使用的次数上,妙啊
想到了贪心,但是倒在part2上,大佬orz
orz
妙啊
过了七个数据,进来就看见前缀和数组也要longlong~~~,无语。
好优雅!!!
orz
根本没想到捏QAQ
tql
orz
sto
%%%`
%%% stO
和我想的方法一样,不过我的那个计算次数算法没你的要优美,学到了
orz
大佬强,为什么老是差分想不到呢,害,脑子不好
用线段树也可以hhorz,算法好美,大佬太强了