[CF1534G]A New Beginning
阿新 • • 發佈:2021-06-21
problem
網格圖上給定 \(n\) 個整點 \((x_i,y_i)\),你初始在 \((0,0)\),每次只能向右或者向上走一格。你在任意位置時可以給任意若干個點打標記,假設當前位置為 \((X,Y)\),每次給第 \(i\) 個點打標記的代價為 \(\max(|X-x_i|,|Y-y_i|)\)。
最小化給所有點打標記的代價和。
\(n\le 8\times 10^5\),\(0\le x_i,y_i\le 10^9\)。
sol
首先對於該題,有如下引理:假設通過點 \((x_i,y_i)\) 的直線 \(y=-x+c\),則最小代價為你走的路線與該直線相交時的代價。這個引理很顯然。
接下來我們把平面旋轉 \(45^{\circ}\)
設 \(f_{x,y}\) 表示在轉換後的圖上,走到 \((x,y)\) 處最小代價。假設前面橫座標最大的位置為 \(x=a\),則 \(f_x\leftarrow f_a\),具體來說,有
\[f_{x,y}=\min\{f_{a,b}\}+w(x,y),y-(x-a)\le b\le y+(x-a) \]其中 \(w(x,y)\) 表示 \(\sum_{x_i'=x}|y_i'-y|\)。這樣 DP
的複雜度為 \(\mathcal O(n\cdot 10^9)\)
首先,\(w(x,y)\) 關於 \(y\) 是凸的,對於 \(f_{a,b}\) 關於 \(b\) 如果是凸的,\(\min\{f_{a,b}\}\) 也是凸的。所以維護凸函式即可。對於 \(\min\{a,b\}\),相當於在凸函式左半邊向左平移 \(x-a\),右半部分向右偏移 \(x-a\),這個偏移可以記錄到全域性中。所有操作用堆維護,由此題性質,最優解從凸函式最小值轉移。複雜度 \(\mathcal O(n\log n)\)。
Code
#include <bits/stdc++.h> typedef long long ll; const int N = 8e5 + 5; int n; struct point { int x, y; } a[N]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { int x, y; scanf("%d%d", &x, &y); a[i] = {x + y, x - y}; } std::sort(a + 1, a + n + 1, [](point a, point b) { return a.x < b.x; }); std::priority_queue<int> pa, pb; ll ans = 0; pa.push(0), pb.push(0); for (int i = 1; i <= n; i++) { ll x = a[i].x, y = a[i].y; ans += std::max({pa.top() - x - y, y + pb.top() - x, 0ll}); if (-pb.top() > y - x) pa.push(y + x), pa.push(y + x), pb.push(-(pa.top() - 2 * x)), pa.pop(); else pb.push(-(y - x)), pb.push(-(y - x)), pa.push(-pb.top() + 2 * x), pb.pop(); } ans /= 2; printf("%lld\n", ans); return 0; }