39 Cat Transport 题解

Cat Transport

题面

小 S 是一个大农场主,他饲养了 \(m\) 只猫,并雇用了 \(p\) 名饲养员。农场中有一条笔直的道路,道路旁有 \(n\) 座山丘,从左到右依次编号为 \(1\)\(n\)。第 \(i\) 座山丘与第 \((i-1)\) 座山丘之间的距离为 \(d_{i}\) 米。所有饲养员都居住在山丘 \(1\)

某天,猫咪们外出玩耍。第 \(i\) 只猫咪前往山丘 \(h_{i}\),并在时间 \(t_{i}\) 结束游玩,随后在山丘 \(h_{i}\) 等待饲养员接它。

饲养员必须接走所有猫咪。每位饲养员从山丘 \(1\) 走向山丘 \(n\),途中不在任何山丘停留,并带走途中每个山丘上所有等待的猫咪。

饲养员的行走速度为 \(1\) 米/单位时间,且他们的运输能力足够强,可以携带任意数量的猫咪。

例如,假设有两座山丘(\(d_{2}=1\))和一只猫咪,该猫咪在时间 \(3\) 结束游玩于山丘 \(2\)\(h_{1}=2\))。若饲养员在时间 \(2\) 或时间 \(3\) 离开山丘 \(1\),则能接到这只猫咪;但若在时间 \(1\) 离开则无法接到。若饲养员在时间 \(2\) 出发,猫咪的等待时间为 \(0\);若在时间 \(3\) 出发,猫咪的等待时间为 \(1\)

你的任务是规划每位饲养员从山丘 \(1\) 出发的时间,使得所有猫咪的等待时间总和最小。

\(2 \le n \le 10^5,\ 1 \le m \le 10^5,\ 1 \le p \le 100, 1 \le d_{i} < 10^4,1 \le h_i \le n,\ 0 \le t_i \le 10^9\)

题解

模拟一下发现这道题的山好像没什么用,只有距离是有用的

我们设 \(A_i\) 表示如果在 \(A_i\) 时刻出发,刚好能够带走猫 \(i\) ,那么 \(A_i = T_i - \sum d_j\)

设出发时刻为 \(t\) ,那么这个饲养员能够接到猫 \(i\) 当且仅当 \(A_i \le t\) ,等待时间为 \(t - A_i\)

如果我们按照 \(A_i\) 将猫排序,那么每次被带走的猫就是连续的一段,更方便我们dp

所以设 \(f(i,j)\) 表示前 \(i\) 个饲养员带走前 \(j\) 个猫的最小等待时间,设 \(s\)\(A\) 的前缀和数组

假设第 \(i\) 个饲养员要带走 \(k + 1 \sim j\) 这一段的猫,那么他最早要在 \(A_j\) 时刻出发,这一段猫的最小等待时间为

\[\sum_{p = k + 1}^j (A_j - A_p) = A_j \times (j - k) - (s_j - s_k) \]

转移方程

\[f(i,j) = \max_{0 \le k < j} \{ f(i - 1, k) + A_j \times (j - k) - (s_j - s_k) \} \]

这个方程暴力转移是 \(O(pm^2)\) 的,考虑优化,发现其中有 \(A_j \times k\) 项,所以不能用单调队列直接写

考虑斜率优化,去掉 \(\min\) 将方程变形

\[f(i - 1, k) + s_k = A_j \times k - A_j \times j + s_j + f(i,j) \]

发现直线的斜率 \(A_j\) 单调增,决策点 \(x\) 坐标 \(k\) 单调增,所以直接普通的斜率优化(任务安排 2)

时间复杂度为 \(O(pm)\)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 1e5 + 10;

int n, m, p;
int q[N];
ll sd[N], a[N], s[N], f[110][N];

double slope (int i, int p, int q) {
    return (double)(f[i - 1][q] + s[q] - f[i - 1][p] - s[p]) / (q - p);
}

int main () {
    cin >> n >> m >> p;
    for (int i = 2; i <= n; i ++) {
        scanf ("%lld", &sd[i]);
        sd[i] += sd[i - 1];
    }
    for (int i = 1; i <= m; i ++) {
        int pos, ed;
        scanf ("%d%d", &pos, &ed);
        a[i] = ed - sd[pos];
    }

    sort (a + 1, a + 1 + m);
    for (int i = 1; i <= m; i ++) {
        s[i] = s[i - 1] + a[i];
    }
	
    //注意初始化
    memset (f, 0x3f, sizeof f);
    f[0][0] = 0;
    
    for (int i = 1; i <= p; i ++) {
        int h = 1, t = 0;
        for (int j = 1; j <= m; j ++) {
            while (h < t && slope (i, q[t - 1], q[t]) >= slope (i, q[t], j - 1)) t --;
            q[ ++ t] = j - 1;
            while (h < t && slope (i, q[h], q[h + 1]) <= a[j]) h ++;
            int k = q[h];
            f[i][j] = f[i - 1][k] + a[j] * (j - k) - s[j] + s[k];
        }
    }

    cout << f[p][m] << endl;

    return 0;
}
posted @ 2025-10-09 21:19  michaele  阅读(7)  评论(0)    收藏  举报