P4655 [CEOI2017]Building Bridges
阿新 • • 發佈:2020-10-26
因為兩橋不能相交,故易得\(dp\)方程:\(dp[i]=\min\{dp[j]+sumw[i-1]-sumw[j]+(h[i]-h[j])^2\}\)
看起來似乎可以斜率優化,可惜\(h[i]\)不單調。
這裡介紹一種很巧妙的做法:李超線段樹。
整理方程:\(dp[i]-h^2[i]-sumw[i-1]=(-2h[j])*h[i]+dp[j]-sumw[j]+h^2[j]\)
發現等式右側的取值是斜率為\(-2h[j]\),截距為\(dp[j]-sumw[j]+h^2[j]\)的直線。
相當於我們維護一個直線集,\(dp[i]\)的最小取值即為\(h[i]\)處的最小取值加\(h^2[i]+sumw[i-1]\)
用李超線段樹維護,時間複雜度\(O(nlogn)\)。
程式碼如下,僅供參考:
#include<bits/stdc++.h> using namespace std; #define int long long const int maxn=1e5+10; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f; } int n,h[maxn],sw[maxn],dp[maxn],lim,tr[maxn<<6]; struct node{int k,b;}p[maxn]; inline int calc(int id,int pos){ if(id==0)return 1e16; return p[id].k*pos+p[id].b; } inline void update(int h,int l,int r,int x){ if(!tr[h])return tr[h]=x,void(); int mid=(l+r)>>1; int s=calc(tr[h],mid),t=calc(x,mid); if(l==r){ if(s>t)tr[h]=x; return; } if(p[tr[h]].k<p[x].k){ if(s<t)update(h<<1,l,mid,x); else update(h<<1|1,mid+1,r,tr[h]),tr[h]=x; }else{ if(s<t)update(h<<1|1,mid+1,r,x); else update(h<<1,l,mid,tr[h]),tr[h]=x; } } inline int query(int h,int l,int r,int x){ int mid=(l+r)>>1,val=calc(tr[h],x); if(l==r)return val; if(mid>=x)return min(val,query(h<<1,l,mid,x)); else return min(val,query(h<<1|1,mid+1,r,x)); } signed main(){ n=read(); for(int i=1;i<=n;i++){ h[i]=read(); lim=max(lim,h[i]); } for(int i=1;i<=n;i++) sw[i]=read()+sw[i-1]; dp[1]=0;p[1]=(node){-2*h[1],dp[1]-sw[1]+h[1]*h[1]}; update(1,0,lim,1); for(int i=2;i<=n;i++){ dp[i]=query(1,0,lim,h[i])+sw[i-1]+h[i]*h[i]; p[i]=(node){-2*h[i],dp[i]-sw[i]+h[i]*h[i]}; update(1,0,lim,i); } printf("%lld\n",dp[n]); return 0; }
深深地感到自己的弱小。