LOJ2483. 「CEOI2017」Building Bridges (李超树+DP)
题意
有n个建筑,每个建筑有两个权值(h[i],w[i]) ,h[i]表示建筑的高度,w[i]表示拆除建筑的费用.
现在要在除了头尾之外的n-2个建筑内选择若干个保留,并且保留头尾的建筑.
这样的费用为拆除所有没有保留建筑的费用+相邻的保留两个建筑高度差的平方.
求最小费用.
分析
首先有一个简单的dp思路
dp[i] 表示保留建筑i的最小费用.
初始化:dp[1]=0
答案:dp[n]
转移方程:
然后就能获得40分的好成绩
然后来处理一下这个式子.
展开
整理
然后就很斜率优化了.
令
每次查询就是给出一个x
一个解决方法是CDQ分治
[但是我写不动
一个解决方法是treap
[但是我不会
可以发现需要操作的只有插入线和询问点的最大值.
然后就直接李超树维护一下,区间范围为[-2e6,0]
之后就没有了.
因为是全局插入所以插入线复杂度是log的.[然而如果是固定区间更新的话,复杂度是
的
而查询单点是
的.
所以复杂度是
实现比较容易而且跑得快.
[至于李超树怎么操作请去看别的博客 写的都挺好的qwq?
code
为什么没有高亮
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define pb push_back
#define A first
#define B second
#define pii pair<int,int>
#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 (x<y)x=y;
}
void Max(ll &x,ll y){
if (x<y)x=y;
}
#define M 100005
const ll inf=1e17;
ll h[M],dp[M],sum[M],w[M];
int n;
struct P1{
void solve(){
int i,j;
for (i=2;i<=n;i++){
dp[i]=inf;
for (j=1;j<i;j++){
Min(dp[i],dp[j]+(sum[i-1]-sum[j])+(h[j]-h[i])*(h[j]-h[i]));
}
}
printf("%lld\n",dp[n]);
}
}p1;
inline ll chk(ll k,ll b,ll x){
return k*x+b;
}
#define N 2000005
int L,R,rt;
struct Reimu{
ll kk[N],bb[N];
int tot,ls[N],rs[N];
void upd(int l,int r,ll k,ll b,int &p){
if (!p){
p=++tot;
kk[p]=k;
bb[p]=b;
return;
}
if (chk(k,b,l)<=chk(kk[p],bb[p],l)&&chk(k,b,r)<=chk(kk[p],bb[p],r)){
kk[p]=k;
bb[p]=b;
return ;
}
if (chk(k,b,l)>chk(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;
}