BZOJ 1911: [Apio2010]特别行动队 斜率优化dp

1911: [Apio2010]特别行动队

题目连接:

http://www.lydsy.com/JudgeOnline/problem.php?id=1911

Description

有n个数,然后你需要把这n个数分成若干段,每段的权值是a*sum*sum+b*sum+c

问你怎么切,使得权值和最大

sum表示该区间的权值和

Input

三行 n,a,b,c 然后n个数,表示每个数的权值

Output

答案

Sample Input

4

-1 10 -20

2 2 3 4

Sample Output

9

数据范围n,b,c为1e6,a为5

Hint

题意

题解:

斜率优化dp

n^2 dp很显然,dp[i] = max(dp[j]+w[i][j])

这个模式比较显然的是斜率优化dp的模式

方程,假设i>j,且i优于j,则可以化为 dp[i]-dp[j]+a*(sum[i]*sum[i]-sum[j]*sum[j])+b*(sum[j]-sum[i]) / 2*a*(sum[i]-sum[j]) <sum[i]

然后斜率优化dp跑一波就好了

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int n;
long long a,b,c;
long long x;
long long sum[maxn];
long long dp[maxn];
double slope(int i,int j)
{
    double up = dp[i]-dp[j]+a*(sum[i]*sum[i]-sum[j]*sum[j])+b*(sum[j]-sum[i]);
    double down = 2*a*(sum[i]-sum[j]);
    return up/down;
}
int l,r,q[maxn];
int main()
{
    scanf("%d",&n);
    scanf("%lld%lld%lld",&a,&b,&c);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        sum[i]=sum[i-1]+x;
    }
    for(int i=1;i<=n;i++)
    {
        while(l<r&&slope(q[l+1],q[l])<sum[i])l++;
        int now = q[l];
        dp[i]=dp[now]+a*(sum[i]-sum[now])*(sum[i]-sum[now])+b*(sum[i]-sum[now])+c;
        while(l<r&&slope(q[r],q[r-1])>slope(i,q[r]))r--;
        q[++r]=i;
    }
    printf("%lld\n",dp[n]);
}
posted @ 2016-02-03 14:38  qscqesze  阅读(304)  评论(0编辑  收藏  举报