CF1534G A New Beginning
简单的,有趣的,简短的。
睡觉前找了道题当睡前故事,回去 yy 了一会儿就想出来了。
首先切比雪夫距离可以转换为路径与过关键点的斜率为 \(-1\) 的直线的交点处的距离。
证:画个图就知道了,相交之前距离单调不增,相交之后距离单调不减。
那么可以将关键点按照 \(x_i + y_i\) 排序,设 \(f_{i, j}\) 表示现在在第 \(i\) 个点对应的直线上,横坐标为 \(j\),前 \(i\) 个关键点带来的代价最小值。
转移:
\[f_{i, j} = \min_{j - d \leq k \leq j} \{f_{i - 1,k}\} + |x_i - j|
\]
\(d\) 表示 \(x_i + y_i - x_{i - 1} - y_{i - 1}\)
思考一下这玩意的本质,后面的绝对值是一个关于 \(j\) 的下凸包,那 \(f_i\) 肯定也是关于 \(j\) 的下凸包,于是尝试维护凸包而不是 dp 值。
每次操作相当于把最小值右侧的图像右移 \(d\),然后加上一个分段函数。
坏了,我不会,但我会差分(或者求导?)。
加上 \(|x_i - j|\) 等价与在导函数的左半部分 \(-1\),右半部分 \(+1\),可以用 multiset 维护区间导函数的相对关系,同时记录最小值区间, 移动右侧图像直接移动最小值右端点即可。
写的很丑。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 8e5 + 10;
const ll inf = INT64_MAX;
class Point {
public:
ll x, y;
Point() = default;
Point(ll x, ll y) : x(x), y(y) {}
}p[maxn];
#define fir first
#define sec second
multiset<pair<ll, ll>> L, R;
ll sL, sR;
ll l, r, cur;
int n;
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> p[i].x >> p[i].y;
}
sort(p + 1, p + n + 1, [](const Point &a, const Point &b) {
return a.x + a.y < b.x + b.y;
});
l = r = p[1].x;
L.emplace(0ll, l);
R.emplace(0ll, p[1].x + p[1].y - r);
cur = sL = sR = 0;
for(int i = 2; i <= n; i++) {
r += p[i].x + p[i].y - p[i - 1].x - p[i - 1].y;
if(l <= p[i].x && p[i].x <= r) {
sL -= p[i].x - l;
sR -= r - p[i].x;
L.emplace(sL, p[i].x - l + sL);
R.emplace(sR, r - p[i].x + sR);
l = p[i].x, r = p[i].x;
} else if(p[i].x < l) {
pair<ll, ll> cut = *prev(L.lower_bound(make_pair(l - p[i].x + sL, inf)));
L.erase(L.find(cut));
L.emplace(cut.fir, l - p[i].x + sL);
L.emplace(l - p[i].x + sL, cut.sec);
L.emplace(l - p[i].x + sL, l - p[i].x + sL);
cur += l - p[i].x;
sR -= r - l;
R.emplace(sR, r - l + sR);
cut = *L.begin();
L.erase(L.begin());
r = l;
l -= cut.sec - cut.fir;
sL = cut.sec;
}else {
pair<ll, ll> cut = *prev(R.lower_bound(make_pair(p[i].x - r + sR, inf)));
R.erase(R.find(cut));
R.emplace(cut.fir, p[i].x - r + sR);
R.emplace(p[i].x - r + sR, cut.sec);
R.emplace(p[i].x - r + sR, p[i].x - r + sR);
cur += p[i].x - r;
sL -= r - l;
L.emplace(sL, r - l + sL);
cut = *R.begin();
R.erase(R.begin());
l = r;
r += cut.sec - cut.fir;
sR = cut.sec;
}
}
cout << cur;
}