ARC147C Min Diff Sum
Sol:
很妙的一道题。
将限制转化为线段讨论。
首先考虑一个特殊情况:当所有线段都相交的时候,答案显然为 \(0\).
那么假如有两条线段没有相交呢?
我们可以将是否相交的判定转化为 \(l_{max}\) 和 \(r_{min}\) 的大小关系。
-
\(l_{max} \le r_{min}\) : 答案为 \(0\).
-
\(l_{max} > r_{min}\) : 这个时候我们考虑分解成子问题,并递归解决。此时 \(l_{max}\) 所处的线段肯定与 \(r_{min}\) 不同,将这两条线段 \(i, j\) 的贡献拉出来单独计算,并递归计算剩下的线段贡献。
观察这两条线段与其他线段间产生的贡献:$\sum_{x \ne i, x\ne j}{|a_i - a_x| + |a_j - a_x|} + |a_i - a_j| $
最小化前面的柿子,当 \(a_x\) 取到 \([r_j, l_i]\) 中间某值时贡献肯定最小,这个时候通过画图可以发现:所有 \(a_x\) 都可以取到 \([r_j, l_i]\) 间,于是产生的贡献为 \((V + 1) \times |a_i - a_j|\) (\(V\) 为剩下的线段数),那么显然 \(a_i = l_i, a_j = r_j\) 时贡献最小。
前面的递归处理也不需要了,只需要排序后一个循环即可。
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, l[N], r[N];
bool cmp(int x, int y){return x > y;}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n; int ans = 0;
for(int i = 1; i <= n; i++) cin >> l[i] >> r[i];
sort(l + 1, l + n + 1, cmp); sort(r + 1, r + n + 1);
for(int i = 1; i <= n; i++) ans += max(0ll, l[i] - r[i]) * (n - 2 * i + 1);
cout << ans;
return 0;
}