CodeForces 1901F Landscaping

发布时间 2023-12-07 18:55:27作者: zltzlt

洛谷传送门

CF 传送门

还是很有趣的一道题。场上直接暴拆式子,要维护动态凸包,本来以为是 \(\log^2\) 的,写着写着发现是 \(\log^3\),遂弃。

显然梯形面积最小等价于 \(y_0 + y_1\) 最小,而 \(y_0 + y_1\) 最小等价于梯形在 \(m = \frac{n}{2}\) 处最小。

把上凸包建出来,发现\(x = m\) 的线段,所在直线在 \(x = m\) 处最小。

又因为过 \(x = m\) 的线段 \(x_1 = l, x_2 = r\) 一定满足 \(l \le m < r\),所以即等价于求 \(\forall l \le m < r, (l, a_l), (r, a_r)\)\(x = m\) 处的最大值

于是我们砍半,对于 \([0, m]\) 的询问,我们把 \(a\)\([m + 1, n]\) 的上凸包建出来,然后对于一个询问 \(i\),我们可以二分然后取前缀 \(\max\) 处理出 \([0, i]\)\(b\) 的点和上凸包的点构成的线段在 \(x = m\) 的最大值,和 \([i + 1, m]\)\(a\) 的点和上凸包的点构成的线段在 \(x = m\) 的最大值。

对于 \([m + 1, n]\) 的询问类似。

所以总时间复杂度就是 \(O(n \log n)\)

code
// Problem: F. Landscaping
// Contest: Codeforces - Educational Codeforces Round 158 (Rated for Div. 2)
// URL: https://codeforces.com/problemset/problem/1901/F
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 200100;

ll n, a[maxn], b[maxn], m;
ldb f[maxn], g[maxn];
struct node {
	ldb x, y;
	node(ldb a = 0, ldb b = 0) : x(a), y(b) {}
} p[maxn];

int stk[maxn], top;

inline node operator - (const node &a, const node &b) {
	return node(a.x - b.x, a.y - b.y);
}

inline ldb operator * (const node &a, const node &b) {
	return a.x * b.y - a.y * b.x;
}

inline ldb calc(node a, node b) {
	ldb k = (a.y - b.y) / (a.x - b.x);
	ldb t = a.y - a.x * k;
	return k * n / 2 + t;
}

inline ldb calc(node x) {
	int l = 1, r = top;
	while (r - l > 1) {
		int mid = (l + r) >> 1;
		if (calc(x, p[stk[mid]]) < calc(x, p[stk[mid + 1]])) {
			l = mid + 1;
		} else {
			r = mid;
		}
	}
	ldb ans = -1e18;
	for (int i = l; i <= r; ++i) {
		ans = max(ans, calc(x, p[stk[i]]));
	}
	return ans;
}

void solve() {
	scanf("%lld", &n);
	--n;
	m = n / 2;
	for (int i = 0; i <= n; ++i) {
		scanf("%lld", &a[i]);
	}
	for (int i = 0; i <= n; ++i) {
		scanf("%lld", &b[i]);
	}
	for (int i = m + 1; i <= n; ++i) {
		p[i] = node(i, a[i]);
		while (top >= 2 && (p[stk[top]] - p[stk[top - 1]]) * (p[i] - p[stk[top - 1]]) >= 0) {
			--top;
		}
		stk[++top] = i;
	}
	g[m + 1] = -1e18;
	for (int i = 0; i <= m; ++i) {
		f[i] = max(i ? f[i - 1] : -1e18, calc(node(i, b[i])));
	}
	for (int i = m; i; --i) {
		g[i] = max(g[i + 1], calc(node(i, a[i])));
	}
	for (int i = 0; i <= m; ++i) {
		printf("%.12Lf ", max(f[i], g[i + 1]) * 2);
	}
	top = 0;
	for (int i = 0; i <= m; ++i) {
		p[i] = node(i, b[i]);
		while (top >= 2 && (p[stk[top]] - p[stk[top - 1]]) * (p[i] - p[stk[top - 1]]) >= 0) {
			--top;
		}
		stk[++top] = i;
	}
	f[m] = g[n + 1] = -1e18;
	for (int i = m + 1; i <= n; ++i) {
		f[i] = max(f[i - 1], calc(node(i, b[i])));
	}
	for (int i = n; i > m; --i) {
		g[i] = max(g[i + 1], calc(node(i, a[i])));
	}
	for (int i = m + 1; i <= n; ++i) {
		printf("%.12Lf ", max(f[i], g[i + 1]) * 2);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}