【基础排序】CF - 最优排列Permutator
题目描述
Manuel 和 Felix 在 TeamsCode 比赛前一晚一直在寻找一个有趣的数学题。 他们最终找到了一个问题:
Omer 有两个长度相同的整数数组 aaa 和 bbb。
他可以重新排列数组 bbb 的顺序,以最小化以下表达式的值:
∑i=0n−1∑j=in−1∑k=ijak⋅bk\sum_{i=0}^{n-1}\sum_{j=i}^{n-1}\sum_{k=i}^{j} a_k \cdot b_k i=0∑n−1j=i∑n−1k=i∑jak⋅bk
换句话说,Omer 想要通过重新排列 bbb,使得对于所有子数组 [i,j][i, j][i,j], ∑k=ijak⋅bk\sum_{k=i}^{j} a_k \cdot b_k∑k=ijak⋅bk 的总和尽可能小。
请你帮助他计算出在最优排列下,这个表达式的最小可能值。
输入格式
第一行输入一个整数 nnn,表示两个数组的长度。
第二行输入 nnn 个整数 a0,a1,…,an−1a_0, a_1, \dots, a_{n-1}a0,a1,…,an−1,表示数组 aaa。
第三行输入 nnn 个整数 b0,b1,…,bn−1b_0, b_1, \dots, b_{n-1}b0,b1,…,bn−1,表示数组 bbb。
输出格式
输出一个整数,表示通过重新排列 bbb 所能得到的最小值。
样例 #1
输入
3
5 4 -1
4 3 2
输出
65
样例说明
一种最优的排列方式是将 bbb 重新排列为:b=[3,2,4]b = [3, 2, 4]b=[3,2,4]
此时表达式的值为最小,结果为 656565。
数据范围
- 2≤n≤1052 \le n \le 10^52≤n≤105
- −100≤ai,bi≤100-100 \le a_i, b_i \le 100−100≤ai,bi≤100
提交链接
Permutator
思路分析
🧩 我们有两个数组 a 和 b, 可以重新排列数组 b 的顺序, 目标是让下式的值尽可能小:
∑i=0n−1∑j=in−1∑k=ijak⋅bk\sum_{i=0}^{n-1} \sum_{j=i}^{n-1} \sum_{k=i}^{j} a_k \cdot b_k i=0∑n−1j=i∑n−1k=i∑jak⋅bk
看上去三重循环非常吓人 😵,但其实我们可以把它“化简”!
🧮 化简推导过程
我们来观察每个 ( ak⋅bka_k \cdot b_kak⋅bk ) 在式子中出现了多少次。
位置 kkk 会被多少个子区间 [i, j] 包含呢?
👉 当左端点 ( i≤ki \le ki≤k ) 且右端点 ( j≥kj \ge kj≥k) 时,它才会被选中。
左端点 ( iii ) 可以取 ( 1∼k1 \sim k1∼k),有 ( kkk) 种;
右端点 ( jjj) 可以取 ( k∼nk \sim nk∼n),有 ( n−k+1n - k + 1n−k+1) 种。
所以每个位置 ( kkk ) 的贡献次数是:
ck=k×(n−k+1)c_k = k \times (n - k + 1) ck=k×(n−k+1)
于是整个式子就可以改写为:
总和=∑k=1nck⋅ak⋅bk\text{总和} = \sum_{k=1}^{n} c_k \cdot a_k \cdot b_k 总和=k=1∑nck⋅ak⋅bk
🔢 构造新的数组
我们把每个 ( aka_kak) 都乘上它的出现次数,构造新数组:
ak′=ak×k×(n−k+1)a'_k = a_k \times k \times (n - k + 1) ak′=ak×k×(n−k+1)
于是问题就变成了:重新排列 b,使得:∑ak′⋅bk\sum a'_k \cdot b_k∑ak′⋅bk 的值最小。
🧠 重排不等式登场!
根据著名的 重排不等式(Rearrangement Inequality):
- 如果两个序列同向排序(都升序或都降序) → 点积最大 💥
- 如果两个序列反向排序(一个升序一个降序) → 点积最小 🧊
因此,我们要:
✅ 把 a' 升序排列
✅ 把 b 降序排列
然后一一配对相乘即可。
💻 代码实现
#include <bits/stdc++.h>
using namespace std;const int maxn = 1e5 + 9;
long long a[maxn], b[maxn];
int n;int main() {cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];a[i] = a[i] * i * (n - i + 1); // 加权}sort(a + 1, a + n + 1); // a' 升序for (int i = 1; i <= n; i++)cin >> b[i];sort(b + 1, b + n + 1, greater<int>()); // b 降序long long sum = 0;for (int i = 1; i <= n; i++)sum += a[i] * b[i];cout << sum;return 0;
}