P4983 忘情

题目描述

你的\(npy\)为了恶心你,特地请了四位大神和一个辣鸡!

\(\rm hdxrie\)说:“我们得求和。”于是有了\(\sum\limits_{i=1}^{n}x_i\)

\(\rm Imagine\)说:“我们得有平均数。”于是有了\(\bar x\)

\(\rm TimeTraveller\)说:“我们得有加减乘除。”于是有了一些恶心的组合。

\(\rm Althen·Way·Satan\)说:“我们还得有平方。”于是我们将它平方。

最垃圾的\(\rm ZredXNy\)说:“那我帮你们整合一下。”

于是,我们得到了这么一个式子\(:\)

\(\frac{\left((\sum\limits_{i=1}^{n}x_i×\bar x)+\bar x\right)^2}{\bar x^2}\)

我们定义一段序列的值为这个,其中\(n\)为此序列的元素个数。

我们给定一段长度为\(n\)的序列,现在要求将它分成\(m\)段,要求每一段的值的总和最小,求出这个最小值。

题解

这道题神似P5308 [COCI2019] Quiz。

首先看到分成\(k\)段,很自然地可以想到\(WQS\)二分。

\(\frac{\left((\sum\limits_{i=1}^{n}x_i×\bar x)+\bar x\right)^2}{\bar x^2}\)化简一下可得等于\((\sum_{i = 1} ^ {n}x_i + 1) ^ 2\)

\(f_i\)表示到第\(i\)个数的最小价值和,很容易得到转移方程\(f_i = min(f_i, f_j + (s_i - s_j + 1) ^ 2)\)

假设\(k < j < i\)\(j\)\(k\)优,可以化简得到\(\frac{f_j + s_j ^ 2 - (f_k + s_k ^ 2)}{s_j - s_k} <= 2 * (s_i + 1)\)

然后就\(WQS\)二分就好。

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
const int ll inf = 1e15;
int n, m, a[N], g[N], q[N];
ll ans, f[N], s[N];
inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
ll Y(int x) {return f[x] + s[x] * s[x];}
ll X(int x) {return s[x];}
double slope(int i, int j) {return (double)(Y(i) - Y(j)) / (double)(X(i) - X(j));}
int check(ll mid)
{
	int l, r; q[l = r = 1] = 0;
	for(int i = 1; i <= n; i ++)
	{
		while(l < r && slope(q[l], q[l + 1]) <= 2.0 * (double)(s[i] + 1.0)) l ++;
		f[i] = f[q[l]] + (s[i] - s[q[l]] + 1) * (s[i] - s[q[l]] + 1) + mid; g[i] = g[q[l]] + 1;// + mid
		while(l < r && slope(q[r], q[r - 1]) >= slope(q[r - 1], i)) r --;
		q[++ r] = i;
	}
	return g[n] >= m;
}
void work()
{
	n = read(); m = read();
	for(int i = 1; i <= n; i ++) {a[i] = read(); s[i] = s[i - 1] + a[i];}
	ll l = -inf, r = inf;
	while(l <= r)
	{
		ll mid = (l + r) >> 1;
		if(check(mid)) ans = mid, l = mid + 1;
		else r = mid - 1;
	}
	check(ans); printf("%lld\n", f[n] - ans * m);
}
int main() {return work(), 0;}
posted @ 2020-04-03 10:51  Sunny_r  阅读(125)  评论(0编辑  收藏  举报