题目链接:LOJ 6620

已知两个 $n$ 维实向量 $\vec{a} = (a_{1}, a_{2}, \ldots, a_{n}), \vec{b} = (b_{1}, b_{2}, \ldots, b_{n})$,定义 $n$ 个定义域为 $\mathbb{R}$ 函数 $f_{1}, f_{2}, \ldots, f_{n}$:

$$ f_k(x) = \sum_{i = 1} ^ {k} \lvert a_i x + b_i \rvert \quad (k = 1, 2, \ldots, n) $$

现在,对于每个 $k = 1, 2, \ldots, n$,试求 $f_k$ 在 $\mathbb{R}$ 上的最小值。可以证明最小值一定存在。

数据范围:$1 \le n \le 5 \times 10 ^ 5$,$\lvert a_i \rvert, \lvert b_i \rvert < 10 ^ 5$。


Solution

$f_k(x)$ 的最小值等价于求 $-\frac{b_1}{a_1}, -\frac{b_2}{a_2}, \ldots, -\frac{b_k}{a_k}$ 的带权中位数,对向量按照 $-\frac{b_i}{a_i}$ 排序后在线段树上单点加 $a_i$ 并二分即可。

时间复杂度:$\mathcal O(n \log n)$。


Code

#include <bits/stdc++.h>
typedef long long int64;

const int N = 5e5;
int n, a[N + 5], b[N + 5], id[N + 5], pos[N + 5];

struct Segment {
    #define ls u << 1
    #define rs u << 1 | 1
    #define lson ls, l, mid
    #define rson rs, mid + 1, r
    int64 sum[2][1 << 20 | 1];
    void modify(int x, int w, int o, int u, int l, int r) {
        if (l == r) {
            sum[o][u] += w;
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid) {
            modify(x, w, o, lson);
        } else {
            modify(x, w, o, rson);
        }
        sum[o][u] = sum[o][ls] + sum[o][rs];
    }
    int64 query(int x, int y, int o, int u, int l, int r) {
        if (x == l && r == y) {
            return sum[o][u];
        }
        int mid = (l + r) >> 1;
        if (y <= mid) {
            return query(x, y, o, lson);
        } else if (x > mid) {
            return query(x, y, o, rson);
        } else {
            return query(x, mid, o, lson) + query(mid + 1, y, o, rson);
        }
    }
    int find(int64 w, int u, int l, int r) {
        if (l == r) {
            return l;
        }
        int mid = (l + r) >> 1;
        if (sum[0][ls] >= w) {
            return find(w, lson);
        } else {
            return find(w - sum[0][ls], rson);
        }
    }
    inline int find(int64 w) {
        return find(w, 1, 1, n);
    }
    inline void modify(int x, int w, int o) {
        modify(x, w, o, 1, 1, n);
    }
    inline int64 query(int l, int r, int o) {
        return l > r ? 0 : query(l, r, o, 1, 1, n);
    }
    #undef ls
    #undef rs
    #undef lson
    #undef rson
} T;

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", &b[i]);
        if (a[i] < 0) {
            a[i] = -a[i];
            b[i] = -b[i];
        }
    }
    std::iota(id + 1, id + n + 1, 1);
    std::sort(id + 1, id + n + 1, [](int x, int y) {
        return (int64)b[x] * a[y] > (int64)b[y] * a[x];
    });
    for (int i = 1; i <= n; i++) {
        pos[id[i]] = i;
    }
    int64 sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += a[i];
        T.modify(pos[i], a[i], 0);
        T.modify(pos[i], b[i], 1);
        int p = T.find((sum + 1) / 2);
        double x = 1.0 * -b[id[p]] / a[id[p]];
        printf("%.10lf\n", (T.query(1, p, 0) - T.query(p + 1, n, 0)) * x + T.query(1, p, 1) - T.query(p + 1, n, 1));
    }
    return 0;
}
最后修改:2020 年 12 月 22 日
如果觉得我的文章对你有用,请随意赞赏