[CF319C] Kalila and Dimna in the Logging Industry

Description

砍伐高度为 (a_1,a_2,...,a_n)(n) 棵树。每次他们对编号为 (i) 的树使用电锯,会使第 (i) 个树的高度降低 (1)。每次使用电锯后需要给它充电。充电成本取决于已完全锯掉的树木的编号(树木高度等于 (0) 时,我们说树木被完全锯掉 )。如果已经被完全锯掉的树的最大编号是 (i) ,则对电锯充电一次的成本将是 (b_i) 。电锯在开始时是充好电的。保证对于 (a,b) 的严格单调性以及 (b_n = 0,a_1 = 1) 。要以最低成本完全锯掉所有的树,计算这个最小代价。

Solution

只要我们锯掉第 (n) 棵树那么剩下的部分代价都是 (0),因此我们只需要考虑锯掉第 (n) 棵树的最小代价

(f[i]) 表示锯掉第 (i) 棵树的最小代价,则有

[f[i]=min_{1le i <j } (f[i]+a[i]b[j]) ]

对于 (j le k),若 (k) 优于 (j) 则有 (f[j]+a[i]b[j] > f[k]+a[i]b[k]),化为斜率形式为

[frac {(-f[k])-(-f[j])} {b[k]-b[j]} < a[i] ]

(a[i]) 单调递增,因此我们用单调队列维护下凸包即可

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1000005;

int n,a[N],b[N],f[N],q[N],head,tail;

// Slope DP Utilities

int getx(int i)
{
    return b[i];
}

int gety(int i)
{
    return -f[i];
}

int getk(int i)
{
    return a[i];
}

double getslope(int j,int k)
{
    return 1.0 * (gety(k)-gety(j)) / (getx(k)-getx(j));
}

signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];

    // Push (1) into the queue
    head=tail=0;
    q[0]=1;

    for(int i=2;i<=n;i++)
    {
        while(head<tail && getslope(q[head],q[head+1]) <= getk(i))
        {
            ++head;
        }
        f[i] = f[q[head]] + a[i]*b[q[head]];
        while(head<tail && getslope(q[tail-1],q[tail]) >= getslope(q[tail],i))
        {
            --tail;
        }
        q[++tail] = i;
    }

    cout<<f[n]<<endl;
}

原文地址:https://www.cnblogs.com/mollnn/p/13288362.html