洛谷 P4655 [CEOI2017]Building Bridges
有 \(n\) 根柱子依次排列,每根柱子都有一个高度。第 \(i\) 根柱子的高度为 \(h_i\)。
现在想要建造若干座桥,如果一座桥架在第 \(i\) 根柱子和第 \(j\) 根柱子之间,那么需要 \((h_i-h_j)^2\) 的代价。
在造桥前,所有用不到的柱子都会被拆除,因为他们会干扰造桥进程。第 \(i\) 根柱子被拆除的代价为 \(w_i\),注意 \(w_i\) 不一定非负,因为可能政府希望拆除某些柱子。
现在政府想要知道,通过桥梁把第 \(1\) 根柱子和第 \(n\) 根柱子连接的最小代价。注意桥梁不能在端点以外的任何地方相交。
首先考虑dp,设\(f_i\)表示第\(i\)个柱子和第\(1\)个柱子连接所需要的最小代价,那么可以写出状态转移方程:
\[f_i=Min_{j=1}^{i-1}f_j+sw_{i-1}-sw_j+(h_i-h_j)^2
\]
其中\(sw_i=\sum_{j=1}^iw_j\),然后对式子稍微变一下形:
\[f_i=sw_{i-1}+h_i^2+Min_{j=1}^{i-1}-2h_jh_i+f_j-sw_j+h_j
\]
后面的部分可以看成一条关于\(h_i\)的线段,于是我们可以直接用李超树维护。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e6 + 10;
const long long inf = 1e16;
using namespace std;
int n,h[N + 5],w[N + 5],cc;
long long sw[N + 5],f[N + 5];
double b[N + 5],k[N + 5];
double val(int id,int x)
{
if (!id)
return inf;
return k[id] * x + b[id];
}
bool cmp(int a,int b,int x)
{
double fa = val(a,x),fb = val(b,x);
return fa < fb;
}
struct Seg
{
#define zrt k << 1
#define yrt k << 1 | 1
int mx[N * 5 + 5];
void update(int k,int l,int r,int x,int y,int nw)
{
if (r < x || l > y)
return;
if (l >= x && r <= y)
{
if (cmp(mx[k],nw,l) && cmp(mx[k],nw,r))
return;
if (cmp(nw,mx[k],l) && cmp(nw,mx[k],r))
{
mx[k] = nw;
return;
}
int mid = l + r >> 1;
if (cmp(nw,mx[k],mid))
swap(nw,mx[k]);
if (cmp(nw,mx[k],l))
update(zrt,l,mid,x,y,nw);
else
update(yrt,mid + 1,r,x,y,nw);
return;
}
int mid = l + r >> 1;
update(zrt,l,mid,x,y,nw);
update(yrt,mid + 1,r,x,y,nw);
}
int query(int k,int l,int r,int x)
{
if (x < l || x > r)
return 0;
if (l == r && l == x)
return mx[k];
int mid = l + r >> 1,res;
if (x <= mid)
res = query(zrt,l,mid,x);
else
res = query(yrt,mid + 1,r,x);
if (cmp(mx[k],res,x))
res = mx[k];
return res;
}
}tree;
int main()
{
scanf("%d",&n);
for (int i = 1;i <= n;i++)
scanf("%d",&h[i]);
for (int i = 1;i <= n;i++)
{
scanf("%d",&w[i]);
sw[i] = sw[i - 1] + w[i];
}
cc++;
k[cc] = -2 * h[1];
b[cc] = -sw[1] + 1.0 * h[1] * h[1];
tree.update(1,0,N,0,N,cc);
for (int i = 2;i <= n;i++)
{
int num = tree.query(1,0,N,h[i]);
f[i] = sw[i - 1] + 1ll * h[i] * h[i] + val(num,h[i]);
cc++;
k[cc] = -2 * h[i];
b[cc] = f[i] - sw[i] + 1.0 * h[i] * h[i];
tree.update(1,0,N,0,N,cc);
}
cout<<f[n]<<endl;
return 0;
}