【BZOJ 3156】防御准备

传送门


Problem

在这里插入图片描述


Solution

我们设 f ( i ) f(i) f(i) 为处理到第 i i i 个位置,且第 i i i 个位置放塔的最小花费,设 S ( i ) S(i) S(i) i i i 的前缀和(即 S ( i ) = ∑ j = 1 i j S(i)=\sum_{j=1}^ij S(i)=j=1ij)。

那么根据题意,有以下的转移方程:

f ( i ) = min ⁡ j = 1 i − 1 { f ( j ) + ∑ k = j + 1 i − 1 ( i − k ) + a i } f(i)=\min_{j=1}^{i-1}\{f(j)+\sum_{k=j+1}^{i-1}(i-k)+a_i\} f(i)=j=1mini1{f(j)+k=j+1i1(ik)+ai}

再进一步化简一下,得到:

f ( i ) = min ⁡ j = 1 i − 1 { f ( j ) + ( i − j − 1 ) × i − ( S ( i − 1 ) − S ( j ) ) + a i } f(i)=\min_{j=1}^{i-1}\{f(j)+(i-j-1)\times i-(S(i-1)-S(j))+a_i\} f(i)=j=1mini1{f(j)+(ij1)×i(S(i1)S(j))+ai}

于是我们按照套路,讨论 k &lt; j &lt; i k&lt;j&lt;i k<j<i,且 j j j k k k 更优的条件,即:

f ( j ) + ( i − j − 1 ) × i − ( S ( i − 1 ) − S ( j ) ) + a i &lt; f ( k ) + ( i − k − 1 ) × i − ( S ( i − 1 ) − S ( k ) ) + a i f(j)+(i-j-1)\times i-(S(i-1)-S(j))+a_i&lt;f(k)+(i-k-1)\times i-(S(i-1)-S(k))+a_i f(j)+(ij1)×i(S(i1)S(j))+ai<f(k)+(ik1)×i(S(i1)S(k))+ai

化简得:

f ( j ) + S ( j ) − ( f ( k ) + S ( k ) ) j − k &lt; i \frac{f(j)+S(j)-(f(k)+S(k))}{j-k}&lt;i jkf(j)+S(j)(f(k)+S(k))<i

由于 i i i 是单调不减的,我们就直接单调队列维护然后转移就可以了。


Code

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值