更好的閱讀體驗 【Press Here】
Problem
題目大意:
按順序給定 個子任務,每個任務用時 ,費用係數
連續的多個(一個)子任務合成爲大任務,大任務的用時和費用係數爲所有子任務之和,啓動一個大任務需要時間 ,每次進行一個大任務,其費用爲 結束時間 * 費用係數 ,問最小費用爲多少(大任務也要按照先後順序進行)
要求所有子任務都被包括在大任務之中
題面複雜得一匹…
Solution
因爲根據習慣我們用 來進行動態規劃,所以將 互換一下
先從比較簡單的開始
設 爲 前綴和
設 爲 前綴和
設 爲 將前 個小任務分成若干個大任務所需要的最小費用
我們先不考慮每次機器啓動的 ,易得轉移
經過觀察發現,每次加入一個 都會對後面每個點增加一個貢獻 ,即
所以轉移方程化爲
顯然這樣就能使用 的算法 通過 的數據
那麼如何進一步降低複雜度呢?
假設現在 狀態從 狀態轉移比 狀態要優,則有
化簡可得
其實這就已經可以做了,顯然需要維護一個下凸包,在插入的時候彈出無用狀態
但是後面的斜率 沒有單調性,所以說就不能夠從狀態頭部彈出節點,不能保證頭部狀態最優,所以在可能的狀態序列中二分查找,直到右側節點不優於當前節點爲止
代碼
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000010;
#define mid ((l + r) >> 1)
ll t[N] , g[N];
ll f[N];
ll sta[N] , top;
ll n , s;
ll read() {
ll ans = 0 , flag = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') flag = - 1; ch = getchar();}
while(ch >= '0' && ch <= '9') {ans = ans * 10 + ch - '0'; ch = getchar();}
return ans * flag;
}
bool check(int x , int i) {
if(x == top) return 0;
if(f[sta[x + 1]] - f[sta[x]] <= (g[sta[x + 1]] - g[sta[x]]) * (s + t[i])) return 1;
return 0;
}
bool slope(int x , int y , int z) {
return (f[x] - f[y]) * (g[y] - g[z]) <= (f[y] - f[z]) * (g[x] - g[y]);
}
int work(int i) {
int l = 1 , r = top;
if(l == r) return sta[l];
while(l < r) {
if(check(mid , i)) l = mid + 1;
else r = mid;
}
return sta[l];
}
int main() {
n = read(); s = read();
for(int i = 1 ; i <= n ; ++ i) {
t[i] = read(); t[i] += t[i - 1];
g[i] = read(); g[i] += g[i - 1];
}
sta[++ top] = 0;
for(int i = 1 ; i <= n ; ++ i) {
int j = work(i);
f[i] = f[j] + t[i] * (g[i] - g[j]) + s * (g[n] - g[j]);
while(top >= 2) {
if(slope(i , sta[top] , sta[top - 1])) sta[top --] = 0;
else break;
}
sta[++ top] = i;
}
printf("%lld\n" , f[n]);
return 0;
}