LOJ2483. 「CEOI2017」Building Bridges (李超樹+DP)
阿新 • • 發佈:2018-12-24
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;
}