https://www.luogu.com.cn/problem/P4983
首先对柿子化简过后长这样
[(1+sum x)^2
]
考虑一个naive的DP,(f_{i,j})表示前(i)个数分成(j)段的最小值,然后不难用斜率优化将其优化到(O(nm))。
根据直觉这玩意儿显然有凸性
考虑wqs二分,同样的在dp时斜率优化即可,复杂度(O(n log V))
但如果你二分的是整数的话可能需要注意一些细节,如果当二分切线斜率后切到了凸包的一条边上要优先取横坐标较小的那个点,不然可能找不到合法点。(如果是下面代码中那种二分的话)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N = 1e5 + 10;
ll a[N], s[N], dp[N];
int n, m;
inline db X(int i) { return s[i] - 1; }
inline db Y(int i) { return dp[i] + (s[i] - 1) * (s[i] - 1); }
inline db slope(int i, int j) {
return (Y(j) - Y(i)) / (X(j) - X(i));
}
int calc(ll mid) {
static int cnt[N], q[N];
dp[0] = cnt[0] = 0;
int *ql = q + 1, *qr = q;
*++qr = 0;
for (int i = 1; i <= n; ++i) {
while (ql < qr && 2.0 * s[i] > slope(*ql, *(ql + 1))) ++ql;
int j = *ql;
dp[i] = dp[j] + (s[i] - s[j] + 1) * (s[i] - s[j] + 1) - mid;
cnt[i] = cnt[j] + 1;
while (ql < qr && slope(*(qr - 1), *qr) > slope(*(qr - 1), i)) --qr;
*++qr = i;
}
return cnt[n];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
s[i] = s[i - 1] + a[i];
}
ll l = -1e16, r = 0, p = 1;
while (l <= r) {
ll mid = (l + r) >> 1;
if (calc(mid) <= m) l = mid + 1, p = mid;
else r = mid - 1;
}
calc(p);
printf("%lld
", dp[n] + p * m);
return 0;
}