1. 程式人生 > 其它 >「JOISC 2022 Day1」京都觀光 題解

「JOISC 2022 Day1」京都觀光 題解

Solution

考慮從\((x_1,y_1)\)走到\((x_2,y_2)\)滿足只改變一次方向,則容易求出先向南走當且僅當

\[\frac{a_{x_1} - a_{x_2}}{x_1 - x_2}<\frac{b_{x_1} - b_{x_2}}{x_1 - x_2} \]

我們思考,沒有用的行或列滿足什麼條件。

以行為例,考慮有三行:\(x=i, x=j,x=k\),兩列:\(y=l,y=r\),從\((i,l)\)走到\((k,r)\)我們的條件是在\(x=j\)上走過一定更劣,故而能將\(x=j\)刪去

假設我們從經\(x=j\)走到了,那麼可以得到

\[\frac{a_{i} - a_{j}}{i - j}<\frac{b_{l} - b_{r}}{l - r} \]

到這裡有一個比較容易想到的構造:若

\[\frac{a_{i} - a_{j}}{i - j}>\frac{a_{j} - a_{k}}{j - k} \]

則不會從\((j,l)\)經過\(x=j\)走到\((k,r)\)

所以我們要對\((i,a_i)\)維護出一個斜率單調遞增的凸包,那麼在凸包上的行就是可能有用的

對於列,同理

然後呢?如何求答案?

需要注意的是,在求答案的時候不能一次跳一個矩形的兩條邊,因為我們上面的條件是在只改變一次方向的情況下的,正確的方式是在\((x,y)\)的位置通過比較開頭的式子,來決定先往哪個方向走,但是隻走一段,因為通過去掉重複的部分可以發現走出的每一步都需要依賴於開頭的式子,而走出一步後,狀態發生改變,所以要重新找出最優決策。可以通過維護兩個指標實現,複雜度\(O(H+W)\)

Code

#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
inline LL read() {
	LL res = 0, f = 0; char ch = getchar();
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = getchar()) res = (res << 1) + (res << 3) + (ch ^ 48);
	if(f) res = ~res + 1;
	return res;
}
const int N = 1000100;
LL h, w, a[N], b[N], ans, stk[N], top, p[N], q[N];
inline double slope1(LL x, LL y) {return 1.0 * (a[x] - a[y]) / (x - y);}
inline double slope2(LL x, LL y) {return 1.0 * (b[x] - b[y]) / (x - y);}
inline LL calc(LL x, LL y, LL nx, LL ny) {
	if(x == nx) return a[x] * (ny - y);
	else return b[y] * (nx - x);
}
int main() {
	h = read(), w = read();
	for(int i = 1; i <= h; ++i) a[i] = read();
	for(int i = 1; i <= w; ++i) b[i] = read();
	for(int i = 1; i <= h; ++i) {
		while(top > 1 && slope1(i, stk[top - 1]) < slope1(stk[top], stk[top - 1])) --top;
		stk[++top] = i;
	}
	for(int i = 1; i <= top; ++i) p[i] = stk[i]; 
	top = 0;
	for(int i = 1; i <= w; ++i) {
		while(top > 1 && slope2(i, stk[top - 1]) < slope2(stk[top], stk[top - 1])) --top;
		stk[++top] = i;
	}
	for(int i = 1; i <= top; ++i) q[i] = stk[i];
	for(int p1 = 1, p2 = 1, x = 1, y = 1; !(x == h && y == w); ) {
		if(x == h) ans += calc(x, y, x, q[++p2]), y = q[p2];
		else if(y == w) ans += calc(x, y, p[++p1], y), x = p[p1];
		else {
			if(slope1(p[p1], p[p1 + 1]) < slope2(q[p2], q[p2 + 1])) ans += calc(x, y, p[++p1], y), x = p[p1];
			else ans += calc(x, y, x, q[++p2]), y = q[p2];
		}
	}
	printf("%lld\n",ans);
}