hdu3507 Print Article

hduoj

可以很容易得写出线性dp转移 d p [ i ] = m i n { ( s [ i ] − s [ j ] ) 2 + d p [ j ] + M ) } ( j ∈ [ 0 , i − 1 ] ) dp[i]=min\{ (s[i]-s[j])^2+dp[j]+M)\}(j\in[0,i-1]) dp[i]=min{(s[i]s[j])2+dp[j]+M)}(j[0,i1]),其中s[]为前缀和数组

那么直接把平方拆开,把只与j有关的放到斜率优化dp式子的左边。

可以得到式子 d p [ j ] + s [ j ] 2 = 2 s [ i ] s [ j ] − s [ i ] 2 + d p [ i ] − M dp[j]+s[j]^2=2s[i]s[j]-s[i]^2+dp[i]-M dp[j]+s[j]2=2s[i]s[j]s[i]2+dp[i]M

很显然这个式子 y = d p [ j ] + s [ j ] 2 , x = s [ j ] , k = 2 ∗ s [ i ] y=dp[j]+s[j]^2,x=s[j],k=2*s[i] y=dp[j]+s[j]2,x=s[j],k=2s[i],那么我们要让dp[i]尽可能小就是让与y轴交点尽可能小,而斜率是逐渐增大的,且(x,y)中的x和y都是逐渐增大的,所以我们维护一个下凸包(开口向上),用一个队列维护凸包上的点,头结点为当前斜率下的最小值,且队列构成一个斜率逐渐变大的凸包就行了,不断弹出头结点以获取最优值

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;

const int maxl=5e5+10;

int n,m,cas,k,cnt,tot;ll ans;
ll a[maxl],sum[maxl],dp[maxl];
struct node
{
	ll x,y;
}s[maxl];
bool in[maxl]; 

inline void prework()
{
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
} 

inline ll calc(ll k,int id)
{
	return s[id].y-k*s[id].x;
}

inline bool cmpk(node a,node b,node c)
{
	return 1.0*(b.y-a.y)*(c.x-b.x)>=1.0*(b.x-a.x)*(c.y-b.y);
}

inline void mainwork()
{
	int hd=1,tl=0;ll k;
	node d=node{0,0};
	s[++tl]=d;
	for(int i=1;i<=n;i++)
	{
		while(hd<tl && calc(2*sum[i],hd)>=calc(2*sum[i],hd+1))
			hd++;
		dp[i]=calc(2*sum[i],hd)+sum[i]*sum[i]+m;
		d=node{sum[i],dp[i]+sum[i]*sum[i]};
		while(hd<tl && cmpk(s[tl-1],s[tl],d))
			--tl;
		s[++tl]=d;
	}
}

inline void print()
{
	printf("%lld\n",dp[n]);
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	while(~scanf("%d%d",&n,&m))
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值