APIO 2010题解

APIO 2010题解

P3628 特别行动队

思路:

斜率优化dp,主要以推公式为主,设s为前缀和数组,可得
f [ i ] = f [ j ] + a ∗ s [ i ] ∗ s [ i ] + a ∗ s [ j ] ∗ s [ j ] − 2 ∗ a ∗ s [ i ] ∗ s [ j ] + b ∗ s [ i ] − b ∗ s [ j ] + c f[i]=f[j]+a∗s[i]*s[i]+a*s[j]*s[j]-2∗a∗s[i]∗s[j]+b∗s[i]−b∗s[j]+c f[i]=f[j]+as[i]s[i]+as[j]s[j]2as[i]s[j]+bs[i]bs[j]+c
f [ j ] + a ∗ s [ j ] ∗ s [ j ] − b ∗ s [ j ] = 2 ∗ a ∗ s [ i ] ∗ s [ j ] + f [ i ] − a ∗ s [ i ] ∗ s [ i ] − b ∗ s [ i ] − c f[j]+a*s[j]*s[j]-b*s[j]=2*a*s[i]*s[j]+f[i]-a*s[i]*s[i]-b*s[i]-c f[j]+as[j]s[j]bs[j]=2as[i]s[j]+f[i]as[i]s[i]bs[i]c
看成一次函数解析式,可得纵坐标为 f [ j ] + a ∗ s [ j ] ∗ s [ j ] − b ∗ s [ j ] f[j]+a*s[j]*s[j]-b*s[j] f[j]+as[j]s[j]bs[j],横坐标为 s [ j ] s[j] s[j], 斜率 k = 2 ∗ a ∗ s [ i ] k=2*a*s[i] k=2as[i],要使f[i]最大,也就是使截距最大,先画个图:
凸包
由于题目规定a<0,所以斜率肯定小于0,那么要截距最大我们就需要用单调队列维护一个上凸包,如上图所示

代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

typedef long long LL;

const int N = 1e6 + 10;

LL f[N], s[N]; 
int n, a, b, c;
int q[N];

double X(int i) //横坐标
{
	return s[i];
}

double Y(int i) //纵坐标
{
	return f[i] + a * s[i] * s[i] - b * s[i];
}

double slope(int i, int j) //斜率
{
	return -(1.0 * (Y(i) - Y(j))) / (X(i) - X(j));
}

int main()
{
	scanf("%d%d%d%d", &n, &a, &b, &c);
	
	for (int i = 1; i <= n; i ++ )
	{
		scanf("%lld", &s[i]);
		s[i] += s[i - 1];
	}
	
	int hh = 0, tt = 0;
	q[tt] = 0;
	for (int i = 1; i <= n; i ++ ) //维护上凸包
	{
		while (hh < tt && slope(q[hh], q[hh + 1]) <= -2 * a * s[i]) hh ++ ;
		int j = q[hh];
		f[i] = f[j] + a * (s[i] - s[j]) * (s[i] - s[j]) + b * (s[i] - s[j]) + c;
		while (hh < tt && slope(q[tt], q[tt - 1]) >= slope(i, q[tt - 1])) tt -- ;
		q[++ tt] = i;
	}
	
	printf("%lld\n", f[n]);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值