Loading

ARC130F 做题记录

一道一百年都不会的题目。

link

设最终序列为 \(b\),可知 \(b\) 是下凸函数。

  • 性质:对于任意整数 \(k\),用斜率为 \(k\) 的直线去切这 \(n\) 个点,经过任意次操作,切到的截距永远不变。
Proof 设操作的点为 $x,y$,中间的点为 $z$。最坏情况下,只有这三个点。

操作后,假设斜率为 \(k\) 的直线切到了点 \(z\) 且截距改变,则有:\(b_z - kz < \min(a_x - kx, a_y - ky)\)

移项得:\(b_z < \min(a_x + k(z - x), a_y - k(y - z))\)

又因为 \(a_x + k(z - x) + a_y - k(y - z) = a_x + a_y + k(2z - (x + y)) = a_x + a_y\)

\(b_z\) 已经是 \(\le \dfrac {a_x + a_y} 2\) 的最小整数了,因此矛盾。

进而可以求出 \(b\) 序列每个数的下界。我们得到了 \(b_x\) 的取值的必要条件,尝试证明其为充分条件

  • 性质:对于 \(x\in[1, n]\),存在一个整数 \(k\),设用斜率为 \(k\) 的直线去切最终凸包得到的截距为 \(t\),那么 \(b_x\) 可以取到 \(t + kx\)
Proof

\(x = 1\) 时显然。

\(k = b_x - b_{x - 1}\),容易得知斜率为 \(k\) 的直线一定能切到点 \(x\)

证毕。

于是可以直接得到结论:

  • 结论:设 \(t_k\) 为斜率为 \(k\) 的直线能切到的截距最小值,那么 \(b_x = \max\limits_{k \in \mathbb {Z}} \{t_k + kx\}\)

求出初始用整数斜率直线能切到的所有点,对于每个 \(i\),找到前驱点和后继点算答案即可。

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
using namespace std;
const ll maxn = 3e5 + 10;
ll n, a[maxn], stk[maxn], top, ans;
ll calc1(ll i, ll j) {return floor(1.0l * (a[j] - a[i]) / (j - i));}
ll calc2(ll i, ll j) {return ceil(1.0l * (a[j] - a[i]) / (j - i));}
int main() {
	scanf("%lld", &n);
	for(ll i = 1; i <= n; i++) {
		scanf("%lld", a + i);
		while(top > 1 && calc1(stk[top - 1], stk[top]) + 1 >= calc2(stk[top], i)) --top;
		stk[++top] = i;
	}
	for(ll i = 1; i < top; i++)
		for(ll j = stk[i] + 1; j < stk[i + 1]; j++)
			a[j] = max(a[stk[i]] + calc1(stk[i], stk[i + 1]) * (j - stk[i]),
			a[stk[i + 1]] - calc2(stk[i], stk[i + 1]) * (stk[i + 1] - j));
	for(ll i = 1; i <= n; i++) ans += a[i];
	printf("%lld", ans);
	return 0;
}
/*
5 3 2 1 2
*/
posted @ 2024-05-30 12:00  Lgx_Q  阅读(7)  评论(0编辑  收藏  举报