【题解】P5785 [SDOI2012]任务安排
思路
斜率优化 + 贡献提前。
设 \(f[i]\) 为前 \(i\) 个任务的最小代价,显然有:
\(f[i] = \min\limits_{j = 0}^{i - 1} f[j] + cost(j + 1, i) + s\)
这里的 \(s\) 可以在斜率优化的时候当成常数项,也可以先贡献提前算出来,也就是把它对于后面的任务的贡献提前累加到状态中,这样就可以消去 \(s\).
考虑对 \(T\) 做前缀和,对 \(S\) 作后缀和,有状态转移方程:
\(f[i] = \min\limits_{j = 0}^{i - 1} f[j] + (S + T_i - T_j) S_{j + 1}\)
拆开得到:
\(f[i] = \min\limits_{j = 0}^{i - 1} T_i S_{j + 1} + (f[j] - T_j S_{j + 1} + s S_{i + 1})\)
令 \(S_{j + 1}\) 为斜率,\(T_i\) 为自变量,此时贡献即为关于 \(T_i\) 的一次函数,在下凸包上二分即可。
时间复杂度 \(O(n \log n)\)
代码
#include <cstdio>
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 3e5 + 5;
struct line
{
ll k, b;
db px;
ll gety(ll x) { return k * x + b; }
} q[maxn];
int n, c, top;
int t[maxn], s[maxn];
ll f[maxn];
double interx(line a, line b) { return a.k == b.k ? (a.b > b.b ? 1e18 : -1e18) : 1.0 * (b.b - a.b) / (a.k - b.k); }
void add(line l)
{
while ((top > 1) && interx(l, q[top]) < q[top].px) top--;
q[++top] = l, q[top].px = interx(q[top], q[top - 1]);
}
ll query(int v)
{
int l = 1, r = top;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (q[mid].px <= v) l = mid;
else r = mid - 1;
}
return q[l].gety(v);
}
int main()
{
scanf("%d%d", &n, &c);
for (int i = 1; i <= n; i++) scanf("%d%d", &t[i], &s[i]);
for (int i = 1; i <= n; i++) t[i] += t[i - 1];
for (int i = n; i >= 1; i--) s[i] += s[i + 1];
for (int i = 0; i <= n; i++) s[i] = s[i + 1];
add((line){s[0], 1ll * c * s[0]});
for (int i = 1; i <= n; i++)
{
f[i] = query(t[i]);
add((line){s[i], f[i] + 1ll * (c - t[i]) * s[i]});
}
printf("%lld\n", f[n]);
return 0;
}