【HNOI 2008】玩具装箱

本文探讨了一种区间分割算法,旨在解决序列分割问题,通过计算每个区间的贡献值并求其最小值。采用动态规划结合下凸壳优化技巧,实现了高效求解。适用于序列分割、区间贡献计算等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


Problem

给出一个长度为 n n n 的序列 { a n } \{a_n\} {an} 以及常数 m m m,现要将它分成若干段,对于一个 [ i , j ] [i,j] [i,j] 的区间的贡献是 ( j − i + ∑ k = i j a k − m ) 2 (j-i+\sum\limits_{k=i}^ja_k-m)^2 (ji+k=ijakm)2,求总贡献的最小值。

数据范围: 1 ≤ n ≤ 50000 1\le n\le50000 1n50000 1 ≤ n , a i ≤ 1 0 7 1\le n,a_i\le10^7 1n,ai107


Solution

我们定义 S i S_i Si a i a_i ai 的前缀和, f i f_i fi 为在 i i i i + 1 i+1 i+1 分割一次的最小费用。

那么有

f i = min ⁡ j = 1 i − 1 { f j + ( i − j − 1 + ( S i − S j ) − m ) 2 } f_i=\min_{j=1}^{i-1}\{f_j+(i-j-1+(S_i-S_j)-m)^2\} fi=j=1mini1{fj+(ij1+(SiSj)m)2}

那我们令 G i = S i + i G_i=S_i+i Gi=Si+i(为了方便化简),得:

f i = min ⁡ j = 1 i − 1 { f j + ( G i − G j − ( m + 1 ) ) 2 } f_i=\min_{j=1}^{i-1}\{f_j+(G_i-G_j-(m+1))^2\} fi=j=1mini1{fj+(GiGj(m+1))2}

于是选择任意的 k &lt; j &lt; i k&lt;j&lt;i k<j<i,当 j j j k k k 优时,有(接下来就是把平方拆开然后化简,就不写了):

( f j + G j 2 + 2 ( m + 1 ) G j ) − ( f k + G k 2 + 2 ( m + 1 ) G k ) G j − G k &lt; 2 G i \frac{(f_j+G_j^2+2(m+1)G_j)-(f_k+G_k^2+2(m+1)G_k)}{G_j-G_k}&lt;2G_i GjGk(fj+Gj2+2(m+1)Gj)(fk+Gk2+2(m+1)Gk)<2Gi

然后维护一个下凸壳转移即可。


Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define ll long long
using namespace std;
int n,m,Q[N];
ll S[N],G[N],f[N];
ll Squ(ll x)  {return x*x;}
ll ordi(int x)  {return f[x]+Squ(G[x])+2ll*(m+1)*G[x];}
double slope(int x,int y)  {return 1.0*(ordi(y)-ordi(x))/(G[y]-G[x]);}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1,x;i<=n;++i){
		scanf("%d",&x),S[i]=S[i-1]+x,G[i]=S[i]+i;
	}
	int l=0,r=0;
	for(int i=1;i<=n;++i){
		while(l<r&&slope(Q[l],Q[l+1])<=2*G[i])  l++;
		f[i]=f[Q[l]]+Squ(G[i]-G[Q[l]]-(m+1));
		while(l<r&&slope(Q[r-1],Q[r])>=slope(Q[r],i))  r--;
		Q[++r]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值