Loading

【题解】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;
}
posted @ 2023-01-13 21:30  kymru  阅读(47)  评论(0编辑  收藏  举报