Snowdrop修长廊

描述

Snowdrop现在是世界著名的工程师。因为重庆的天气太热了,
Snowdrop决定修一条长廊,并且长廊要覆盖必须覆盖的n个点。为
了简化整个问题,我们把一条路抽象成一维的,原点在0,有n个必须
覆盖的点,每个必须覆盖的点的坐标为,覆盖点的代价可以设成
(),其中,为固有花费。现在,
他想知道覆盖所有必须的点所需要的最小花费是多少。

输入

第一行输入一个T表示有T组样例
接下来
每块第一行输入
接下来一行描述个点的距离原点位置

输出

输出最小花费

样例输入

1
10 5000
1
23
45
67
101
124
560
789
990
1019

样例输出

30726


【题意】中文题面。

【分析&解题思路】 裸斜率优化,就是要注意到dp[i]=dp[j-1]+(sum[i]-sum[j])^2里面,不包含j.dp[i]有初值,但是斜率优化不会考虑他自己是最优值的情况。

【AC代码】

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <stdio.h>
#include <iostream>
#include <algorithm>
typedef long long LL;
using namespace std;
const int maxn = 500010;
LL dp[maxn],sum[maxn],q[maxn];
LL n,m,head,tail;
LL get_DP(int j,int k)
{
    return dp[k-1]+m+(sum[j]-sum[k])*(sum[j]-sum[k]);
}
LL get_UP(int j,int k) //yj-yk部分
{
    return dp[j-1]+sum[j]*sum[j]-(dp[k-1]+sum[k]*sum[k]);
}
LL get_DOWN(int j,int k) //xj-xk部分
{
    return 2*(sum[j]-sum[k]);
}
int main()
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        scanf("%I64d%I64d",&n,&m);
        for(int i=1; i<=n; i++) scanf("%I64d",&sum[i]);
        sum[0]=dp[0]=0;
        head=tail=0;
        q[tail++] = 0;
        //斜率优化
        for(int i=1; i<=n; i++)
        {
            //求解过程
            dp[i]=dp[i-1]+m;
            while(head+1<tail&&get_UP(q[head+1],q[head])<=sum[i]*get_DOWN(q[head+1],q[head])) head++;
            dp[i] = min(get_DP(i,q[head]),dp[i]);
            //维护解集
            while(head+1<tail&&get_UP(i,q[tail-1])*get_DOWN(q[tail-1],q[tail-2])<=get_UP(q[tail-1],q[tail-2])*get_DOWN(i,q[tail-1]))
                tail--;
            q[tail++] = i;
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
}