Logo FiraCode 的博客

博客

ABC334F

...
FiraCode
2025-12-01 12:55:22
什么意思呢

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2023-12-24 18:28:04

思路:

由于是按顺序来发礼物,那么显然可以考虑 DP。

那么设 $f_{i}$ 表示发了前 $i$ 个,并且当前在 $0$ 的最小的距离花费,$dist(i,j)$ 表示第 $i$ 个点和第 $j$ 个点的距离,第 $0$ 个点表示起点,$cost(i, j)$,表示 $i$ 按顺序走到 $j$ 点的距离和。

那么 $f_{i} = \min \{f_j + dist(0,j + 1) + cost(j + 1, i) + dist(i, 0)\}$。

然后因为 $cost$ 既有 $i$ 也有 $j$ 不太好维护,所以考虑拆成前缀和:

$f_{i} = \min \{f_j + dist(0,j + 1) + sum_i-sum_j + dist(i, 0)\}$。

然后把与 $i$ 有关的提出来:

$f_{i} = \min \{f_j + dist(0,j + 1)-sum_j\} + dist(i, 0) + sum_i$。

于是 $\min$ 中的直接用线段树做即可(其实也可以单调队列滑动窗口)。

Code:

#include <bits\/stdc++.h>

using namespace std;

int n, k;
pair<double, double> a[200010];
double f[200010], s[200010];
double tr[800010];

void modify(int u, int l, int r, int x, double d) {
    if (l == r) {
        tr[u] = d;
        return;
    }

    int mid = (l + r) >> 1;

    if (x <= mid) modify(u << 1, l, mid, x, d);
    else modify(u << 1 | 1, mid + 1, r, x, d);

    tr[u] = min(tr[u << 1], tr[u << 1 | 1]);
}

double query(int u, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr) return tr[u];

    int mid = (l + r) >> 1;
    double res = 1e18;

    if (ql <= mid) res = min(res, query(u << 1, l, mid, ql, qr));
    if (qr > mid) res = min(res, query(u << 1 | 1, mid + 1, r, ql, qr));

    return res;
}

double dist(pair<double, double> a, pair<double, double> b) {
    return sqrt((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));
}

int main() {
    scanf("%d%d", &n, &k);
    for (int i = 0; i <= n; ++i) scanf("%lf%lf", &a[i].first, &a[i].second);

    for (int i = 0; i <= n; ++i) f[i] = 1e18;
    for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + dist(a[i - 1], a[i]);

    f[0] = 0;
    modify(1, 0, n, 0, f[0] + dist(a[0], a[1]) - s[1]);

    for (int i = 1; i <= n; ++i) {
        f[i] = query(1, 0, n, max(0, i - k), i - 1) + s[i] + dist(a[i], a[0]);
        if (i < n) modify(1, 0, n, i, f[i] - s[i + 1] + dist(a[i + 1], a[0]));
    }

    printf("%.15lf\n", f[n]);
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。