[CF1534G]A New Beginning

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}\),讓座標點 \((x,y)\rightarrow (x',y')=(x+y,x-y)\),這樣對於 \(x\) 相同的點,對應的直線是同一條。

\(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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章