题目大意:
有 n n n 根柱子依次排列,每根柱子都有一个高度。第 i i i根柱子的高度为 h i h_i hi。
现在想要建造若干座桥,如果一座桥架在第 ii 根柱子和第 jj 根柱子之间,那么需要 ( h i − h j ) 2 (h_i-h_j)^2 (hi−hj)2的代价。
在造桥前,所有用不到的柱子都会被拆除,因为他们会干扰造桥进程。第 i i i根柱子被拆除的代价为 w i w_i wi,注意 w i w_i wi 不一定非负,因为可能政府希望拆除某些柱子。
现在政府想要知道,通过桥梁把第 1 1 1根柱子和第 n n n 根柱子连接的最小代价。注意桥梁不能在端点以外的任何地方相交。
题目思路:
这题可以用 斜率优化
d
p
dp
dp,处理凸包来做。
这里介绍李超线段树
的写法:
这种考虑
d
p
dp
dp处理,用
d
p
i
dp_i
dpi代表当前跳到
i
i
i位置时最少的花费,所以就有(
f
j
f_j
fj代表前
w
w
w数组的前
j
j
j项和):
d
p
i
=
m
a
x
(
d
p
j
+
(
h
i
−
h
j
)
2
+
f
i
−
1
−
f
j
,
d
p
i
)
dp_i = max(dp_j+(h_i-h_j)^2 + f_{i-1}-f_j,dp_i)
dpi=max(dpj+(hi−hj)2+fi−1−fj,dpi)
令上述转移方程拆项:
d p i = m a x ( d p j + h j 2 − 2 ∗ h i ∗ h j − f j + h i 2 + f i − 1 ) dp_i = max(dp_j + h_j^2-2*h_i*h_j-f_j + h_i^2 +f_{i-1}) dpi=max(dpj+hj2−2∗hi∗hj−fj+hi2+fi−1)
所以这个值可以讲与 i i i有关的提出来,那么剩下的:
d p j + h j 2 − 2 ∗ h i ∗ h j − f j dp_j + h_j^2-2*h_i*h_j-f_j dpj+hj2−2∗hi∗hj−fj这个最大就好了
如何处理这个最大值呢?
可以将 d p j + h j 2 − 2 ∗ h i ∗ h j − f j dp_j + h_j^2-2*h_i*h_j-f_j dpj+hj2−2∗hi∗hj−fj看成一个一次函数表达式:
- y = k ∗ x + b y = k*x + b y=k∗x+b
- k = − 2 ∗ h j k = -2*h_j k=−2∗hj
- b = d p j + h j 2 − f j b = dp_j + h_j^2-f_j b=dpj+hj2−fj
- x = h i x = h_i x=hi
这样看来只需要用李超线段树维护之前的一次函数,求一下在 h i h_i hi处的最值就可以轻松解决啦!
Code:
/*** keep hungry and calm CoolGuang! ***/
//#pragma GCC optimize(3)
#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll INF= 1e18+7;
const ll maxn = 1e6+700;
const int M = 1e6+8;
const ll mod= 998244353;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
ll n,m,p;
ll num[maxn];
ll h[maxn],w[maxn];
ll sum[maxn];
ll a[maxn*5],b[maxn*5];
int tag[maxn*5];
void Insert(int k,int l,int r,int x,int y,ll c,ll d){
int mid = (l+r)/2;
if(x<=l && y>=r){
if(!tag[k]){
tag[k] = 1,a[k] = c,b[k] = d;
return ;
}
ll opx = a[k]*mid + b[k],opy = c*mid + d;
if(l == r){if(opx<opy) {a[k] =c,b[k] = d;}return;}
if(opx > opy){///旧的比新的小
if(a[k]<c){
Insert(k<<1|1,mid+1,r,x,y,a[k],b[k]);
a[k] = c;b[k] = d;
}else{
Insert(k<<1,l,mid,x,y,a[k],b[k]);
a[k] = c;b[k] = d;
}
}else{
if(a[k]<c) Insert(k<<1,l,mid,x,y,c,d);
else Insert(k<<1|1,mid+1,r,x,y,c,d);
}
return;
}
if(x<=mid) Insert(k<<1,l,mid,x,y,c,d);
if(y>mid) Insert(k<<1|1,mid+1,r,x,y,c,d);
}
ll FindMin(int k,int l,int r,int pos){
if(l == r) return tag[k]?a[k]*pos*1ll+b[k]:INF;
int mid = (l+r)/2;
ll ans = tag[k]?a[k]*pos*1ll+b[k]:INF;
if(pos <= mid) return min(ans,FindMax(k<<1,l,mid,pos));
return min(ans,FindMax(k<<1|1,mid+1,r,pos));
}
int main(){
read(n);
for(int i=1;i<=n;i++) read(h[i]);
for(int i=1;i<=n;i++){
read(w[i]);
sum[i] = sum[i-1] + w[i];
}
ll ans = INF;
Insert(1,0,M,0,M,-2*h[1],h[1]*h[1]-sum[1]);
for(int i=2;i<=n;i++){
ll temp = h[i]*h[i] + sum[i-1] + FindMax(1,0,M,h[i]);
Insert(1,0,M,0,M,-2*h[i],temp+h[i]*h[i]-sum[i]);
ans = temp;
}
printf("%lld\n",ans);
return 0;
}
/***
12152
1 1 0 0 0 0 0 0 0
1 2 3 4 5 6 7 8 9
***/