【BZOJ】1010 玩具裝箱

分析

預處理前綴和

sumi=j=1iai
,爲區間求和作準備。

這顯然是dp。
fi 表示前i 件玩具花的最小費用。
則有:
①邊界條件:f0=0
②動態轉移方程:fi=min(fj+(ij1+sumisumjL)2)
③答案:fn

直接求解,時間複雜度爲O(n2) ,顯然會TLE。
考慮斜率優化

原來的方程太複雜了,我們先化簡一下。
通過換元法化簡動態轉移方程,將參量、變量、常量分離
gi=i+sumigj=j+sumjC=1+L
fi=min(fj+(gigjC)2)

求解斜率方程
爲了加快求解,我們設法儘可能多的排除一些狀態,這需要決策點之間的比較,我們要找出決策點比較的式子。

設當前要求fi ,存在決策點k,jk>j ,滿足決策k 優於決策j
fk+(gigkC)2fj+(gigjC)2
fk+(gkC)2+2gi(gkC)fj+(gjC)2+2gi(gjC)
fk+(gk+C)2fj(gj+C)22gi(gkgj)
k>jsumk>sumj
k+sumk>j+sumjgk>gj
slope(k,j)=fk+(gk+C)2fj(gj+C)2gkgj2gi

不難發現,kj 可以看做兩個定點,不因i 的改變而改變:
(fk+(gk+C)2gk)(fj+(gj+C)2gj)
近一步,iN+ ,決策點i 可以表示爲平面上的點(fi+(gi+C)2gi)

對於k,jN+k>j
①當slope(k,j)2gi 時,決策點k 優於決策點j
②當slope(k,j)>2gi 時,決策點k 劣於決策點j

可以將問題轉化爲平面上的點的問題

現在問題轉化爲:
給定平面上n 個在橫縱座標都單調遞增的點,給定一個斜率k ,支持以下三種操作:
①插入點操作:
在第n 個點(xn,yn) 的末尾增加一個點n+1 ,也滿足xn+1>xnyn+1>yn
②詢問點操作:
在點集X={(x1,y1),(x2,y2),...,(xn,yn)} 中尋找一個點i ,使得經過i 斜率爲k 的直線在其他所有點之下;
③更改k 操作:
k 變成k ,滿足k>k

要尋找的點i ,很明顯i 在點集的下凸殼。
又由於k 是不斷增大的,所以凸殼上要找的點i 的橫座標也在不斷增大。

圖

我們只需要維護一個單調隊列即可。

隊首維護:
隊首的點爲AB ,若slope(B,A)2gi ,則將隊首往後移一位;

隊尾維護:
隊末三個點爲ABC
slope(A,B)>slope(A,C) ,則將B 移出隊。

每個點進隊一次,出隊一次。
所以總的時間複雜度爲O(n) ,空間複雜度也爲O(n)

總結

總算徹底弄清斜率優化的嚴謹過程了。

代碼

#include <cstdio>
#include <cctype>

typedef long long Lint;
const int N=65536;

int n,l;
int c[N];

Lint sum[N];

Lint g[N];
Lint C;
Lint f[N];
Lint x[N];
Lint y[N];

int q[N];
int qh,qt;

inline int Read(void)
{
    int x=0; char c=getchar();
    for (;!isdigit(c);c=getchar());
    for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x;
}

inline double Slope(int i,int j)
{
    return ((double)y[i]-y[j])/(x[i]-x[j]);
}

int main(void)
{
    n=Read(),l=Read();
    for (int i=1;i<=n;i++) c[i]=Read();

    for (int i=1;i<=n;i++) sum[i]=sum[i-1]+c[i];

    C=1+l;
    for (int i=1;i<=n;i++) g[i]=sum[i]+i;
    f[0]=0;
    x[0]=g[0];
    y[0]=f[0]+(g[0]+C)*(g[0]+C);
    q[qh=qt=1]=0;

    for (int i=1;i<=n;i++)
    {
        for (;qh!=qt&&Slope(q[qh+1],q[qh])<=2.0*g[i];q[qh++]=0);
        f[i]=f[q[qh]]+(g[i]-g[q[qh]]-C)*(g[i]-g[q[qh]]-C);
        x[i]=g[i];
        y[i]=f[i]+(g[i]+C)*(g[i]+C);
        for (;qh<qt&&Slope(q[qt-1],i)<Slope(q[qt-1],q[qt]);q[qt--]=0);
        q[++qt]=i;
    }

    printf("%lld\n",f[n]);

    return 0;
}
發佈了65 篇原創文章 · 獲贊 9 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章