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 }

 

posted @ 2016-01-07 23:14  Armeria  阅读(151)  评论(0编辑  收藏  举报