SCU - 4509
使用若干条线段,覆盖坐标轴上的 N个点
覆盖 [i,j] 的代价为 cost(i,j)=W+(xi−xj)2
求覆盖所有点的最小代价
斜率优化入门题,与 HDU - Print Article一致
对坐标排序后,设dp[i]表示已经覆盖前 i个点的最小代价
容易得出DP方程为
dp[i]=min(dp[j−1]+(xi−xj)2)
这是
O(N2)
的转移,所以需要优化
设有
k<j
,且满足
dp[k−1]+(xi−xk)2≥dp[j−1]+(xi−xj)2
那么我们选择从 j 转移,把 k优化掉
通过整理上式,可以得到
(dp[j−1]+x2j)−(dp[k−1]+x2k)2(xj−xk)≤xi
设
Yi=dp[i−1]+x2i
,
Xi=2∗xi
这样左边其实就是一个斜率的式子
Yj−YkXj−Xk≤xi
然后右边
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]);
}