bzoj1010: [HNOI2008]玩具裝箱toy


傳送門

以下結論是顯然的:

dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2)(j<i)

其中dp[i]表示裝前i個東西的最小話費。

但是O(n^2)明顯葯丸(也許松爺一下可以過)

於是令f[i]=sum[i]+i,c=1+l

dp[i]=min(dp[j]+(f[i]-f[j]-c)^2)

然后就是神(gui)奇(chu)的斜率優化了。

1.證明決策單調性

假設在狀態i處的k決策優與j決策,即dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-f[j]-c)^2

則對於i后的所有狀態t,要證明決策單調性,dp[k]+(f[t]-f[k]-c)^2<=dp[j]+(f[t]-f[j]-c)^2

只要證dp[k]+(f[i]+v-f[k]-c)^2<=dp[j]+(f[i]+v-f[j]-c)^2

只要證dp[k]+(f[i]-f[k]-c)^2+2*v*(f[i]-f[k]-c)+v^2<=dp[j]+(f[i]-f[j]-c)^2+2*v*(f[i]-f[j]-c)+v^2

只要證2*v*(f[i]-f[k]-c)<=2*v*(f[i]-f[j]-c)

f[k]>=f[j](顯然)

證明完畢

2.求斜率方程

因為dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-f[j]-c)^2

展開dp[k]+f[i]^2-2*f[i]*(f[k]+c)+(f[k]+c)^2<=dp[j]+f[i]^2-2*f[i]*(f[j]+c)+(f[j]+c)^2

dp[k]-2*f[i]*(f[k]+c)+(f[k]+c)^2<=dp[j]-2*f[i]*(f[j]+c)+(f[j]+c)^2

(dp[k]+(f[k]+c)^2-dp[j]-(f[j]+c)^2)/2*(f[k]-f[j])<=f[i]

f[i]是單調遞增的,我們使用隊列維護一個下凸殼,每次取出隊頭作為決策

加入決策i時,令隊尾為q[r],前一個為q[r-1]

滿足斜率(q[r],i)<斜率(q[r-1],q[r])時,顯然隊尾是無效的,將其彈出。

於是乎我們得到了一個O(n)的dp,八中跑得飛快

/**************************************************************
Problem: 1010
User: zhouyuyang
Language: C++
Result: Accepted
Time:160 ms
Memory:2464 kb
****************************************************************/

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll a[50005],f[50005],q[50005],n,c,l,r;
ll read(ll &x){
x=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
}
ll sqr(ll x){
return x*x;
}
double get(ll j,ll k){
return (f[k]-f[j]+sqr(a[k]+c)-sqr(a[j]+c))/(2.0*(a[k]-a[j]));
}
int main(){
read(n);
read(c);
c++;
for (int i=1;i<=n;i++) read(a[i]);
for (int i=1;i<=n;i++) a[i]=a[i-1]+a[i]+1;
l=1;
r=1;
q[1]=0;
for (int i=1;i<=n;i++){
while (l<r&&get(q[l],q[l+1])<=a[i]) l++;
f[i]=f[q[l]]+sqr(a[i]-a[q[l]]-c);
while (l<r&&get(q[r],i)<get(q[r-1],q[r])) r--;
r++;
q[r]=i;
}
printf("%lld",f[n]);
return 0;
}





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com