【ybt金牌导航1-3-3】【luogu P4072】征途

征途

题目链接:ybt金牌导航1-3-3 / luogu P4072

题目大意

有 n 个值,你要把它划分成 m 段,使得把每段的值加起来得到段的值,段值的方差最小。
输出这个最小方差乘 m^2,可证明这个是整数。

思路

.首先我们考虑化简式子:(设分段之后每一段的长度是 d i d_i di x ‾ \overline{x} x 是它们的平均值,即 x ‾ = ∑ i = 1 m d i m \overline{x}=\frac{\sum_{i=1}^{m}d_i}{m} x=mi=1mdi
s 2 × m 2 s^2\times m^2 s2×m2
= ∑ i = 1 m ( d i − x ‾ ) 2 m × m 2 =\dfrac{\sum\limits_{i=1}^{m}(d_i-\overline{x})^2}{m}\times m^2 =mi=1m(dix)2×m2
= ∑ i = 1 m ( d i − x ‾ ) 2 × m =\sum\limits_{i=1}^{m}(d_i-\overline{x})^2\times m =i=1m(dix)2×m
= ∑ i = 1 m ( d i 2 − 2 × d i × x ‾ + x ‾ 2 ) × m =\sum\limits_{i=1}^{m}(d_i^2-2\times d_i\times \overline{x}+\overline{x}^2)\times m =i=1m(di22×di×x+x2)×m
= m × ∑ i = 1 m d i 2 + ∑ i = 1 m ( − 2 × d i × ∑ i = 1 m d i m + ( ∑ i = 1 m d i m ) 2 ) × m =m\times\sum\limits_{i=1}^{m}{d_i^2}+\sum\limits_{i=1}^{m}(-2\times d_i\times \frac{\sum_{i=1}^{m}d_i}{m}+(\frac{\sum_{i=1}^{m}d_i}{m})^2)\times m =m×i=1mdi2+i=1m(2×di×mi=1mdi+(mi=1mdi)2)×m
= m × ∑ i = 1 m d i 2 + ∑ i = 1 m ( − 2 × d i × ∑ i = 1 m d i + ( ∑ i = 1 m d i m ) ( ∑ i = 1 m d i ) ) =m\times\sum\limits_{i=1}^{m}{d_i^2}+\sum\limits_{i=1}^{m}(-2\times d_i\times \sum_{i=1}^{m}d_i+(\frac{\sum_{i=1}^{m}d_i}{m})(\sum_{i=1}^{m}d_i)) =m×i=1mdi2+i=1m(2×di×i=1mdi+(mi=1mdi)(i=1mdi))

我们再弄一个 s u m = ∑ i = 1 m d i sum=\sum_{i=1}^{m}d_i sum=i=1mdi
= m × ∑ i = 1 m d i 2 + ∑ i = 1 m ( − 2 × d i × s u m + ( s u m m ) × s u m ) =m\times\sum\limits_{i=1}^{m}{d_i^2}+\sum\limits_{i=1}^{m}(-2\times d_i\times sum+(\frac{sum}{m})\times sum) =m×i=1mdi2+i=1m(2×di×sum+(msum)×sum)
= m × ∑ i = 1 m d i 2 + ( − 2 ) × ∑ i = 1 m d i × s u m + ∑ i = 1 m ( 1 m ) × s u m 2 =m\times\sum\limits_{i=1}^{m}{d_i^2}+(-2)\times \sum\limits_{i=1}^{m}d_i\times sum+\sum\limits_{i=1}^{m}(\frac{1}{m})\times sum^2 =m×i=1mdi2+(2)×i=1mdi×sum+i=1m(m1)×sum2
= m × ∑ i = 1 m d i 2 + ( − 2 ) × s u m × s u m + s u m 2 =m\times\sum\limits_{i=1}^{m}{d_i^2}+(-2)\times sum\times sum+sum^2 =m×i=1mdi2+(2)×sum×sum+sum2
= m × ∑ i = 1 m d i 2 − 2 × s u m 2 + s u m 2 =m\times\sum\limits_{i=1}^{m}{d_i^2}-2\times sum^2+sum^2 =m×i=1mdi22×sum2+sum2
= m × ∑ i = 1 m d i 2 − s u m 2 =m\times\sum\limits_{i=1}^{m}{d_i^2}-sum^2 =m×i=1mdi2sum2

那你会发现,你就是要让 ∑ i = 1 m d i 2 \sum\limits_{i=1}^{m}d_i^2 i=1mdi2 最小。

那你可以设 f i , j f_{i,j} fi,j 为前 i i i 个数划分成了 j j j 段的最小值。
那会有这个方程: f i , j = min ⁡ k = 1 i − 1 { f k , j − 1 + ( s i − s k ) 2 } f_{i,j}=\min\limits_{k=1}^{i-1}\{f_{k,j-1}+(s_i-s_k)^2\} fi,j=k=1mini1{fk,j1+(sisk)2}

那可以看出它是有单调性的,那我们就直接上分治,做就好了。

代码

#include<cstdio>
#include<iostream>
#define ll long long

using namespace std;

int n, m;
ll a[3001], s[3001], f[3001][3001];

void slove(int op, int l, int r, int L, int R) {//分治
	int mid = (l + r) >> 1, pl = 0;
	ll minn = 1e15;
	for (int i = L; i <= R && i < mid; i++) {//对于处理范围中间的点用枚举答案范围求答案
		ll now = f[i][op - 1] + (s[mid] - s[i]) * (s[mid] - s[i]);
		if (now < minn) {
			pl = i;
			minn = now;
		}
	}
	
	f[mid][op] = minn;
	
	if (l < mid) slove(op, l, mid - 1, L, pl);
	if (mid < r) slove(op, mid + 1, r, pl, R);
}

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];
	}
	
	for (int i = 1; i <= n; i++)//预处理只有一段的
		f[i][1] = s[i] * s[i];
	for (int i = 2; i <= m; i++)
		slove(i, 1, n, 1, n);
	
	printf("%lld", f[n][m] * m - s[n] * s[n]);//记得套回公式里
	
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值