BZOJ 1010 - 斜率优化
比较裸的DP+斜率优化啦…… 让窝又想到了BZOJ上A的第一道有意义的题1597…… 作为第27个A的题也让我颇有感触……
设前$i$个玩具放置到$j$个盒子里所需的最小费用为$f[i][j]$。由于连续的玩具必须放到一个容器里,所以我们有:$$f[i][j]=f[k][j-1]+cost[k+1][i]$$其中k为我们枚举的上一个容器的末玩具编号。然后$cost[k+1][i]$用前缀和搞搞也可以$O(1)$弄出来:$cost[i+1][j]=(j-i+sum[j]-sum[i]-L-1)^2$。但枚举$k$肯定会TLE… (诶下面这都是些什么… $$( Transformers\ Lost\ Energy)$$$$( Teenagers\ Lack\ Entertainment )$$$$( Teenagers\ Lost\ Eggs )$$$$( 啊好污啊… )$$所以我们需要优化。
首先把第二维去掉:完全不需要。然后参阅了黄学长博客,对斜率优化有了更深的理解。
观察$cost$的表达式,我们令$d[i]=sum[i]+i$,$t=L+1$(少写几个字…),则有$$f[k]+(d[j]-d[k]-t)^2<f[p]+(d[j]-d[p]-t)^2$$
第一步,证明决策单调性。什么意思呢?我们设$f[i]$从$f[k]$转移比从$f[p]$转移来要优,且$p<k$,那么对于$f[i]$之后的任意状态$f[t]$,都有从$f[k]$转移比从$f[p]$转移来要优。证明并不难,只要设$d[t]=d[i]+s$然后搞搞就行了。
第二步,解出斜率方程。$$f[k]+cost[k+1][j] \lt f[p]+cost[p+1][j] $$则有$$f[k]+(d[j]-d[k]-t)^2<f[p]+(d[j]-d[p]-t)^2$$由于我们已经获得了单调递增的函数$d$,化简移项一番(把$d[i]$移到不等式的一边),得到:$$\frac{(f[k]+(d[k]+t)^2)-(f[p]+(d[p]+t)^2)}{2 × (d[k]-d[p])}<=d[i]$$有了上述这些性质,我们用单调队列维护一个下凸包即可。具体来说,每次取出$i$作为当前决策点,然后考虑当前队列。如果$(Q[head], Q[head+1])$的斜率大于$d[i]$,那么将$Q[head]$出队,直到$(Q[head], Q[head+1])$的斜率小于$d[i]$;然后取出$Q[head]$,显然它就是当前决策点。然后再将$i$元素加入队列。这就要求$(Q[tail], i)$的斜率大于$(Q[tail-1], Q[tail])$(单调队列的性质)。状态值$f$在维护队列的过程中顺便维护即可。
一开始没有用long long,然后后来打补丁的时候又各种地方忘改成long long……
1 // BZOJ 1010 2 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 7 const int N=50000+5; 8 9 #define LL long long 10 #define read(x) scanf("%d", &x) 11 #define rep(i,a,b) for (int i=a; i<=b; i++) 12 13 int n, c, L, Q[N]; 14 LL f[N], d[N], t; 15 16 LL sqr(LL x) { return x*x; } 17 18 double calc(int p, int k) { 19 return (double)(f[k]+sqr(d[k]+t)-(f[p]+sqr(d[p]+t)))/(double)(2.0*(d[k]-d[p])); 20 } 21 22 int main() 23 { 24 read(n); read(L); 25 d[0]=0; 26 rep(i,1,n) read(c), d[i]=d[i-1]+c; 27 rep(i,1,n) d[i]+=i; 28 int head=1, tail=1; 29 Q[1]=0; f[0]=0; 30 t=L+1; 31 rep(i,1,n) { 32 while (head<tail && calc(Q[head], Q[head+1])<=d[i]) head++; 33 int x=Q[head]; 34 f[i]=f[x]+sqr(d[i]-d[x]-t); 35 while (head<tail && calc(Q[tail-1], Q[tail])>calc(Q[tail], i)) tail--; 36 Q[++tail]=i; 37 } 38 printf("%lld\n", f[n]); 39 40 return 0; 41 }