[SDOI 2012]任務安排
阿新 • • 發佈:2020-08-08
Description
給定 \(n\) 個任務,第 \(i\) 個任務有兩個引數 \(T_i, C_i\),你現在要將這些任務分為相鄰的若干段,每一段任務需要同時完成。一段任務的用時為 \(\sum T_i+s\)(\(s\) 給定),這一段任務會同時結束。依次執行每一段劃分好的任務。每一個任務的花費為該任務的結束時刻 \(\times C_i\)。問所有劃分中,最少花費和。
\(1\leq n\leq 3\times 10^5, 1\leq s\leq 2^8,0\leq |T_i|,C_i\leq 2^8\)
Solution
將 \(T, C\) 分別做字首和後,設 \(f_i\)
移項,可得 \(sC_j-f_j=-T_iC_j+(T_iC_i+sC_n-f_i)\),要最小化 \(f_i\),即最大化截距。因此將 \((C_j, sC_j-f_j)\) 這些點描繪在二維平面上後,去找到上凸包上與斜率為 \(-T_i\) 相切的點,這一點即為最優決策點。
由於 \(-T_i\) 不滿足單調性,因此需要在凸包上二分。
Code
#include <bits/stdc++.h> #define y(i) (1ll*s*c[i]-f[i]) #define x(i) (1ll*c[i]) #define ll long long using namespace std; const int N = 3e5+5; int n, s, t[N], c[N], q[N], head, tail; ll f[N]; int main() { scanf("%d%d", &n, &s); for (int i = 1; i <= n; i++) scanf("%d%d", &t[i], &c[i]), t[i] += t[i-1], c[i] += c[i-1]; for (int i = 1; i <= n; i++) { int l = head, r = tail-1, ans = tail, m; while (l <= r) { m = (l+r)>>1; if ((y(q[m])-y(q[m+1])) >= -1ll*t[i]*(x(q[m])-x(q[m+1]))) ans = m, r = m-1; else l = m+1; } int j = q[ans]; f[i] = f[j]+1ll*t[i]*(c[i]-c[j])+1ll*s*(c[n]-c[j]); while (head < tail && (y(i)-y(q[tail-1]))*(x(i)-x(q[tail])) <= (y(i)-y(q[tail]))*(x(i)-x(q[tail-1]))) --tail; q[++tail] = i; } printf("%lld\n", f[n]); return 0; }