题解 P4655 [CEOI2017] Building Bridges
题目链接
思维历程
首先应当思考不带优化的 dp,再进行优化。
先列出最朴素的式子。
其中
观察到其中有一项为
简单讲一下李超树的实现,插入直线其实就是对一个区间,首先判断其中点,若新加入直线更优直接交换新旧编号,接下来对两端判断,若新直线(此处表示交换后的
而查询则是正常类似线段树的写法,但是注意要和当前存的直线的取值比较取更优,因为李超树存的其实是可能的最优直线,还是会有例外的,不会的可以左转这里。
首先设
对于第一个点直接处理并加入李超树中,对于之后的点先计算取值再加入李超树。
三个小坑点:
-
记得开
long long。 -
李超树由于叶子节点是区间上的点所以节点空间大小记得开四倍。
-
记得初始化
b_0 同时赋值要大亿点。
本人拙见,如有差错,请赐教。
代码
#include<bits/stdc++.h>
#define ll long long
#define void inline void
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const ll N=1e6+10;
ll n,h[N],sum[N],dp[N],k[N],b[N],pre[N<<2];
inline ll read(){
char ch=getchar();ll res=0,f=1;
while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch&&ch<='9') res=res*10+ch-'0',ch=getchar();
return res*f;
}
inline ll v(ll num,ll x){//对于x在第num条直线上的取值。
return k[num]*x+b[num];
}
void modify(ll rt,ll l,ll r,ll x){//这里是插入第x条直线。
ll mid=(l+r)>>1;
if(v(x,mid)<v(pre[rt],mid)) swap(x,pre[rt]);
if(v(x,l)<v(pre[rt],l)) modify(ls,l,mid,x);
if(v(x,r)<v(pre[rt],r)) modify(rs,mid+1,r,x);
}
inline ll query(ll rt,ll l,ll r,ll x){
if(l==r) return v(pre[rt],x);
ll mid=(l+r)>>1;
if(x<=mid) return min(v(pre[rt],x),query(ls,l,mid,x));//记得取min。
else return min(v(pre[rt],x),query(rs,mid+1,r,x));
}
int main(){
n=read(),b[0]=1e18;//一开始默认第0条,所以必须初始化。
for(ll i=1;i<=n;i++) h[i]=read();
for(ll i=1;i<=n;i++) sum[i]=read(),sum[i]+=sum[i-1];
k[1]=-2*h[1],b[1]=h[1]*h[1]-sum[1],modify(1,0,N,1);//第一条直接插入。
for(ll i=2;i<=n;i++){
dp[i]=query(1,0,N,h[i])+h[i]*h[i]+sum[i-1];
k[i]=-2*h[i],b[i]=dp[i]+h[i]*h[i]-sum[i],modify(1,0,N,i);
}
printf("%lld",dp[n]);
return 0;
}