1 条题解
-
0
自动搬运
来自洛谷,原作者为

hongzy
**搬运于
2025-08-24 21:51:11,当前版本为作者最后更新于2018-12-27 18:50:52,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
首先,这题答案与切的顺序无关
证明很好证,考虑将一块分为三块
方法一:先分成。答案为
方法二:先分成。答案为
答案相同。
然后考虑方程:若,令表示前个数分次的最大得分
$f[i][j]=min_{0\leq l<i}(f[l][j - 1] + s[l] * (s[i] - s[l]))$
需要滚动数组存,以下用表示(前驱状态)
然后考虑当选转移优于时:(以下默认)
$$f[j] + s[j]s[i] - s[j]^2 > f[l] + s[l]s[i] - s[l]^2 $$$$f[j] - s[j]^2 - (f[l] - s[l]^2) > (s[l]- s[j])s[i] $$$$\frac{f[j] - s[j]^2 - (f[l] - s[l]^2)}{- s[j] -(-s[l])} > s[i] $$把每个点看做,那么单调队列维护一个点集,斜率递增
这些点形成了一个在第三象限的下凸包
操作:
队头更新:若则出队(显然)
队尾更新:若则出队
队尾操作的证明(也就是对凸包的证明):
考虑可不可能做队头。若满足刚刚上方的条件,则:
若使得成了队首
这时候当前的比刚刚的还要小,因此将继续出队,无法做队头。
斜率递增,故是一个凸包
还有一个细节:注意可能会出现,导致存在,因此计算斜率应注意横坐标相同的情况,斜率为.
#include <algorithm> #include <cstring> #include <cstdio> using namespace std; typedef long long LL; const int N = 1e5 + 10; int n, k, fa[205][N]; LL s[N], f[N], g[N]; double slope(int a, int b) { LL x1 = - s[a], y1 = g[a] - s[a] * s[a]; LL x2 = - s[b], y2 = g[b] - s[b] * s[b]; if(x1 == x2) return -1e18; return (y2 - y1) / (double) (x2 - x1); } int main() { scanf("%d%d", &n, &k); for(int i = 1; i <= n; i ++) { scanf("%lld", s + i); s[i] += s[i - 1]; } static int q[N], hd, bk; for(int t = 1; t <= k; t ++) { for(int i = 1; i <= n; i ++) g[i] = f[i]; hd = bk = 0; for(int i = 1; i <= n; i ++) { for(; bk - hd >= 2 && slope(q[hd], q[hd + 1]) <= s[i]; hd ++) ; f[i] = 0; if(hd < bk) { int & j = q[hd]; fa[t][i] = j; f[i] = g[j] + s[j] * (s[i] - s[j]); } for(; bk - hd >= 2 && slope(q[bk - 1], q[bk - 2]) >= slope(i, q[bk - 1]); bk --) ; q[bk ++] = i; } } printf("%lld\n", f[n]); for(int x = fa[k][n]; k; x = fa[-- k][x]) printf("%d ", x); return 0; }upd: 感谢@chennengkuan 指出定义的小错误
- 1
信息
- ID
- 1456
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者