[CF319C] Kalila and Dimna in the Logging Industry - 斜率優化dp
阿新 • • 發佈:2020-07-12
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\)
設 \(f[i]\) 表示鋸掉第 \(i\) 棵樹的最小代價,則有
\[f[i]=\min_{1\le 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; }