[SCU 4509] Snowdrop修长廊 (斜率DP)

SCU - 4509

使用若干条线段,覆盖坐标轴上的 N个点
覆盖 [i,j] 的代价为 cost(i,j)=W+(xixj)2
求覆盖所有点的最小代价


斜率优化入门题,与 HDU - Print Article一致
对坐标排序后,设dp[i]表示已经覆盖前 i个点的最小代价
容易得出DP方程为
dp[i]=min(dp[j1]+(xixj)2)
这是 O(N2) 的转移,所以需要优化

设有 k<j ,且满足
dp[k1]+(xixk)2dp[j1]+(xixj)2
那么我们选择从 j 转移,把 k优化掉

通过整理上式,可以得到
(dp[j1]+x2j)(dp[k1]+x2k)2(xjxk)xi
Yi=dp[i1]+x2i Xi=2xi
这样左边其实就是一个斜率的式子 YjYkXjXkxi
然后右边 xi 是单调递增的,所以斜率也是单调递增的
意味着不满足单调递增的点 k都可以被优化掉
所以dp过程中需要用到的点连起来实际上构成了一个下凸包(斜率单调递增)

#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) (a*a)

const int maxn=2e5+10;
int N,W;
LL inpt[maxn];
LL dp[maxn];
int que[maxn];

LL Up(int,int);
LL Down(int,int);

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
//  freopen("out.txt", "w", stdout);
    #endif

    int T;
    scanf("%d", &T);
    for(int ck=1; ck<=T; ck++)
    {
        scanf("%d%d", &N, &W);
        for(int i=1; i<=N; i++) scanf("%lld", &inpt[i]);
        sort(inpt+1,inpt+1+N);
        N=unique(inpt+1,inpt+1+N)-(inpt+1);
        dp[0]=0; dp[1]=W;
        int head=0,tail=1;
        que[0]=1;
        for(int i=2; i<=N; i++)
        {
            while(head+1<tail && 
                Up(que[head], que[head+1]) <= inpt[i]*Down(que[head], que[head+1]))
                head++;
            dp[i]=dp[i-1]+W;
            dp[i]=min(dp[i], dp[que[head]-1]+Sqr((inpt[i]-inpt[que[head]]))+W);
            while(head+1<tail && 
                Up(que[tail-1], i)*Down(que[tail-2], que[tail-1]) <= Up(que[tail-2], que[tail-1])*Down(que[tail-1], i))
                tail--;
            que[tail++]=i;
        }
        printf("%lld\n", dp[N]);
    }
    return 0;
}

LL Up(int k, int j)
{
    return dp[j-1]+Sqr(inpt[j])-(dp[k-1]+Sqr(inpt[k]));
}

LL Down(int k, int j)
{
    return 2*(inpt[j]-inpt[k]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值