BZOJ 1010 - 斜率优化

比较裸的DP+斜率优化啦…… 让窝又想到了BZOJ上A的第一道有意义的题1597…… 作为第27个A的题也让我颇有感触……
设前 i 个玩具放置到j个盒子里所需的最小费用为 f[i][j] 。由于连续的玩具必须放到一个容器里,所以我们有:

f[i][j]=f[k][j1]+cost[k+1][i]
其中k为我们枚举的上一个容器的末玩具编号。然后 cost[k+1][i] 用前缀和搞搞也可以 O(1) 弄出来: cost[i+1][j]=(ji+sum[j]sum[i]L1)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]<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]移到不等式的一边),得到:
(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[tail1],Q[tail]) (单调队列的性质)。状态值 f <script type="math/tex" id="MathJax-Element-16135">f</script>在维护队列的过程中顺便维护即可。
一开始没有用long long,然后后来打补丁的时候又各种地方忘改成long long……

// BZOJ 1010

#include <cstdio>
#include <cstring>
using namespace std;

 const int N=50000+5;

 #define LL long long
 #define read(x) scanf("%d", &x)
 #define rep(i,a,b) for (int i=a; i<=b; i++)

 int n, c, L, Q[N];
 LL f[N], d[N], t;

 LL sqr(LL x) { return x*x; }

 double calc(int p, int k) {
    return (double)(f[k]+sqr(d[k]+t)-(f[p]+sqr(d[p]+t)))/(double)(2.0*(d[k]-d[p]));
 }

int main()
{
    read(n); read(L);
    d[0]=0;
    rep(i,1,n) read(c), d[i]=d[i-1]+c;
    rep(i,1,n) d[i]+=i;
    int head=1, tail=1;
    Q[1]=0; f[0]=0;
    t=L+1;
    rep(i,1,n) {
        while (head<tail && calc(Q[head], Q[head+1])<=d[i]) head++;
        int x=Q[head];
        f[i]=f[x]+sqr(d[i]-d[x]-t);
        while (head<tail && calc(Q[tail-1], Q[tail])>calc(Q[tail], i)) tail--; 
        Q[++tail]=i;
    }
    printf("%lld\n", f[n]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值