「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);
}
posted @ 2022-04-06 21:12  DCH233  阅读(263)  评论(0编辑  收藏  举报