有n个建筑,每个建筑有两个权值(h[i],w[i]) ,h[i]表示建筑的高度,w[i]表示拆除建筑的费用.
现在要在除了头尾之外的n-2个建筑内选择若干个保留,并且保留头尾的建筑.
这样的费用为拆除所有没有保留建筑的费用+相邻的保留两个建筑高度差的平方.
求最小费用.
首先有一个简单的dp思路
dp[i] 表示保留建筑i的最小费用.
s u m [ k ] = ∑ i = 1 k w [ i ] sum[k]=\sum_{i=1}^{k}w[i] sum[k]=∑i=1kw[i]
初始化:dp[1]=0
答案:dp[n]
转移方程:
d p [ i ] = m i n ( d p [ j ] + ( s u m [ i − 1 ] − s u m [ j ] ) + ( h [ i ] − h [ j ] ) 2 ) dp[i]=min(dp[j]+(sum[i-1]-sum[j])+(h[i]-h[j])^2) dp[i]=min(dp[j]+(sum[i−1]−sum[j])+(h[i]−h[j])2)
然后就能获得40分的好成绩
然后来处理一下这个式子.
展开
d p [ j ] + s u m [ i − 1 ] − s u m [ j ] + h [ i ] ∗ h [ i ] + h [ j ] ∗ h [ j ] − 2 ∗ h [ i ] ∗ h [ j ] dp[j]+sum[i-1]-sum[j]+h[i]*h[i]+h[j]*h[j]-2*h[i]*h[j] dp[j]+sum[i−1]−sum[j]+h[i]∗h[i]+h[j]∗h[j]−2∗h[i]∗h[j]
整理
d p [ j ] − s u m [ j ] + h [ j ] ∗ h [ j ] − 2 ∗ h [ i ] ∗ h [ j ] + s u m [ i − 1 ] + h [ i ] ∗ h [ i ] dp[j]-sum[j]+h[j]*h[j] -2*h[i]*h[j] +sum[i-1]+h[i]*h[i] dp[j]−sum[j]+h[j]∗h[j]−2∗h[i]∗h[j]+sum[i−1]+h[i]∗h[i]
然后就很斜率优化了.
令
k = h [ j ] b = d p [ j ] − s u m [ j ] + h [ j ] ∗ h [ j ] k=h[j]\\ b=dp[j]-sum[j]+h[j]*h[j] k=h[j]b=dp[j]−sum[j]+h[j]∗h[j]
每次查询就是给出一个x
x = − 2 ∗ h [ i ] x=-2*h[i] x=−2∗h[i]
一个解决方法是CDQ分治
[但是我写不动
一个解决方法是treap
[但是我不会
可以发现需要操作的只有插入线和询问点的最大值.
然后就直接李超树维护一下,区间范围为[-2e6,0]
之后就没有了.
因为是全局插入所以插入线复杂度是log的.[然而如果是固定区间更新的话,复杂度是 l o g 2 log^2 log2的
而查询单点是 l o g log log的.
所以复杂度是 n log n n\log n nlogn
实现比较容易而且跑得快.
[至于李超树怎么操作请去看别的博客 写的都挺好的qwq?
为什么没有高亮
#include
#define ll long long
#define mp make_pair
#define pb push_back
#define A first
#define B second
#define pii pair
#define lowbit(p) (p&(-(p)))
using namespace std;
void read(int &x){
x=0; char c=getchar(); int p=1;
for (;c<48;c=getchar())if (c=='-')p=-1;
for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
x*=p;
}
void read(ll &x){
x=0; char c=getchar(); int p=1;
for (;c<48;c=getchar())if (c=='-')p=-1;
for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
x*=p;
}
void Min(int &x,int y){
if (x>y)x=y;
}
void Min(ll &x,ll y){
if (x>y)x=y;
}
void Max(int &x,int y){
if (xchk(kk[p],bb[p],l)&&chk(k,b,r)>chk(kk[p],bb[p],r)){
return ;
}
if (l==r){
return ;
}
int mid=(l+r)>>1;
ll lk=kk[p],lb=bb[p];
if (chk(k,b,l)<=chk(lk,lb,l)){
if (chk(k,b,mid)<=chk(lk,lb,mid)){
kk[p]=k;
bb[p]=b;
upd(mid+1,r,lk,lb,rs[p]);
return ;
}
upd(l,mid,k,b,ls[p]);
return ;
}
else{
if (chk(k,b,mid+1)<=chk(lk,lb,mid+1)){
kk[p]=k;
bb[p]=b;
upd(l,mid,lk,lb,ls[p]);
}
upd(mid+1,r,k,b,rs[p]);
}
}
ll qu(int l,int r,int x,int p){
if (!p)return inf;
int mid=(l+r)>>1;
if (x<=mid)return min(chk(kk[p],bb[p],x),qu(l,mid,x,ls[p]));
return min(chk(kk[p],bb[p],x),qu(mid+1,r,x,rs[p]));
}
}T;
struct P2{
ll qu(ll x){
return T.qu(L,R,x,rt);
}
void ins(ll k,ll b){
T.upd(L,R,k,b,rt);
}
void solve(){
int i;
L=-2e6-5;
R=5;
/*
b=(dp[j]-sum[j]+h[j]*h[j])
k=h[j]
x=-2*h[i]
*/
ins(h[1],dp[1]-sum[1]+h[1]*h[1]);
for (i=2;i<=n;i++){
dp[i]=qu(-2*h[i])+h[i]*h[i]+sum[i-1];
ins(h[i],dp[i]-sum[i]+h[i]*h[i]);
}
printf("%lld\n",dp[n]);
}
}p2;
int main(){
// freopen("1.in","r",stdin);
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n);
int i;
for (i=1;i<=n;i++){
read(h[i]);
}
for (i=1;i<=n;i++){
read(w[i]);
sum[i]=sum[i-1]+w[i];
}
if (n<=1000){
p1.solve();
return 0;
}
p2.solve();
return 0;
}