P3195 [HNOI2008]玩具裝箱TOY(斜率優化dp)
阿新 • • 發佈:2019-04-20
玩具 eight sin nbsp https org 問題 div 前綴和
P3195 [HNOI2008]玩具裝箱TOY
設前綴和為$s[i]$
那麽顯然可以得出方程
$f[i]=f[j]+(s[i]-s[j]+i-j-L-1)^{2}$
換下順序
$f[i]=f[j]+(s[i]+i-(s[j]+j+L+1))^{2}$
為了處理方便,我們套路地設
$a[i]=s[i]+i$
$b[i]=s[i]+i+L+1$
於是得出
$f[i]=f[j]+(a[i]-b[j])^{2}$
拆開:$f[i]=f[j]+a[i]^{2}-2*a[i]*b[j]+b[j]^{2}$
移項:$f[j]+b[j]^{2}=2*a[i]*b[j]+f[i]-a[i]^2$
於是我們就把不變量和變量分開了($i$固定)
仔細觀察
$f[j]+b[j]^{2}=2*a[i]*b[j]+f[i]-a[i]^2$
$y=k*x+b$
一次函數!
$y=f[j]+b[j]^{2}$
$k=2*a[i]$($i$遞增時,顯然它是單調遞增的)
$x=b[j]$
$b=f[i]-a[i]^{2}$
如果我們要讓$f[i]$最小,就是讓$b$最小
而對於每個$i$,$k$是不變的
那麽問題就轉化成:找到一個最優的$(x,y)$使$b$最小
考慮到$k$是單調遞增的
於是我們就可以快樂地用單調隊列維護下凸包辣
while(L<R&&K(h[L],h[L+1])<=2*a(i)) ++L;//顯然h[L]不比h[L+1]優,可以刪去 f[i]=f[h[L]]+(a(i)-b(h[L]))*(a(i)-b(h[L]));//計算出最優的f[i] while(L<R&&K(h[R-1],h[R])>K(h[R],i)) --R;//加入點(x[i],y[i])後,h[R]在凸包內部,可以刪去① h[++R]=i;//入隊
①:顯然在加入橙點後,藍點在凸包內部,可以被刪除
#include<iostream> #include<cstdio> #include<cstring> usingnamespace std; typedef double db; #define N 50005 db f[N],s[N]; int n,l,L,R,h[N]; inline db a(int x){return s[x]+x;} inline db b(int x){return s[x]+x+l+1;} inline db X(int x){return b(x);} inline db Y(int x){return f[x]+b(x)*b(x);} inline db K(int x,int y){return (Y(x)-Y(y))/(X(x)-X(y));} int main(){ scanf("%d%d",&n,&l); for(int i=1;i<=n;++i) scanf("%lf",&s[i]),s[i]+=s[i-1]; L=R=1; for(int i=1;i<=n;++i){ while(L<R&&K(h[L],h[L+1])<=2*a(i)) ++L; f[i]=f[h[L]]+(a(i)-b(h[L]))*(a(i)-b(h[L])); while(L<R&&K(h[R-1],h[R])>K(h[R],i)) --R; h[++R]=i; }printf("%.0lf",f[n]); return 0; }
P3195 [HNOI2008]玩具裝箱TOY(斜率優化dp)