LuoguP2605 基站选址

0x01

\(f(i, j)\) 表示第 \(j\) 个基站建在第 \(i\) 个村庄的最小费用,则有

\[f(i, j) = C_i + \min_{1 \leqslant k < i}\{ f(k, j-1) + cost(k, i) \} \]

其中 \(cost(l, r)\) 表示在 \(l,\ r\) 分别建立基站后补偿 \(l\)\(r\) 所有未被覆盖的村庄的花费.

对每个位置 \(k\) 维护 \(f(k, j) + cost (k, i)\),设为 \(v_{k, j} \ (j > 0)\).

\(j = 0\)\(v_{k, j}\) 中的 \(k\) 是无意义的,因此我们只维护一个值 \(v_0\,\).

当枚举到新的 \(i\) 时:

  1. 计算 \(f(i, j) = C_i + \min\{ v_{k, j-1} \}\).
  2. \(j > 0\),加入 \(v_{i, j} = f(i, j)\).
  3. 若存在 \(r_s = i\),对 \(j > 0\),将所有 \(k < l_s\)\(v_{k, j}\)\(v_0\) 增加 \(W_s\,\).
    其中 \(l_i\)\(r_i\) 分别表示能覆盖到 \(i\) 的最左和最右的基站,可预处理.

线段树维护,为节约空间,考虑最外层枚举 \(j\),滚动使用线段树.


0x02 Code

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char c = getchar();
    while(isdigit(c)^1) f &= c != 45, c = getchar();
    while(isdigit(c)) x = (x<<1) + (x<<3) + (c^48), c = getchar();
    return f ? x : -x;
}

const int maxn = 20005;
int d[maxn], c[maxn], s[maxn], w[maxn], l[maxn], r[maxn], f[maxn][105];
vector<int> vec[maxn];

struct SegT {
    int tag[maxn<<2], minv[maxn<<2];
    SegT() {
        memset(minv, 0x3f, sizeof(minv));
    }
    inline void clear(){
        memset(tag, 0, sizeof(tag));
        memset(minv, 0x3f, sizeof(minv));
    }
    inline void pushUp(int p) {
        minv[p] = min(minv[p<<1], minv[p<<1|1]);
    }
    inline void pushDown(int p) {
        minv[p<<1] += tag[p], minv[p<<1|1] += tag[p];
        tag[p<<1] += tag[p], tag[p<<1|1] += tag[p], tag[p] = 0;
    }
    void pModify(int p, int l, int r, int x, int k) {
        if(l == r) {
            minv[p] = k;
            return;
        }
        pushDown(p);
        int mid = l + r >> 1;
        if(x <= mid) pModify(p<<1, l, mid, x, k);
        else pModify(p<<1|1, mid+1, r, x, k);
        pushUp(p);
    }
    void sModify(int p, int l, int r, int x, int y, int k) {
        if(x <= l && r <= y) {
            minv[p] += k, tag[p] += k;
            return;
        }
        pushDown(p);
        int mid = l + r >> 1;
        if(x <= mid) sModify(p<<1, l, mid, x, y, k);
        if(y > mid) sModify(p<<1|1, mid+1, r, x, y, k);
        pushUp(p);
    }
    int query(int p, int l, int r, int x, int y) {
        if(x <= l && r <= y) return minv[p];
        pushDown(p);
        int mid = l + r >> 1, res = 1e9;
        if(x <= mid) res = min(res, query(p<<1, l, mid, x, y));
        if(y > mid) res = min(res, query(p<<1|1, mid+1, r, x, y));
        return res;
    }
}sgt[2];

int main() {
    int n = read(), m = read();
    for(int i = 2; i <= n; ++i) d[i] = read();
    for(int i = 1; i <= n; ++i) c[i] = read();
    for(int i = 1; i <= n; ++i) s[i] = read();
    for(int i = 1; i <= n; ++i) w[i] = read();
    for(int i = 1; i <= n; ++i) {
        l[i] = lower_bound(d+1, d+n+1, d[i] - s[i]) - d;
        r[i] = upper_bound(d+1, d+n+1, d[i] + s[i]) - d - 1;
        assert(l[i] >= 1), assert(r[i] <= n);
        vec[r[i]].push_back(i);
    }
    int v0 = 0, ans = 0;
    for(int i = 1; i <= n; ++i) ans += w[i];
    for(int i = 1; i <= n; ++i) {
        f[i][1] = c[i] + v0;
        sgt[1].pModify(1, 1, n, i, f[i][1]);
        for(int k : vec[i]) v0 += w[k];
    }
    for(int j = 2; j <= m; ++j) {
        int now = j & 1, las = now ^ 1;
        sgt[now].clear();
        for(int i = j; i <= n; ++i) {
            f[i][j] = c[i] + sgt[las].query(1, 1, n, 1, i-1);
            sgt[now].pModify(1, 1, n, i, f[i][j]);
            for(int k : vec[i])
                if(l[k] > 1) sgt[las].sModify(1, 1, n, 1, l[k]-1, w[k]);
        }
        ans = min(ans, sgt[las].minv[1]);
    }
    for(int i = m; i <= n; ++i)
        for(int k : vec[i])
            if(l[k] > 1) sgt[m & 1].sModify(1, 1, n, 1, l[k]-1, w[k]);
    ans = min(ans, sgt[m & 1].minv[1]);
    printf("%d\n", ans);
    return 0;
}
posted @ 2023-08-29 13:21  johnsmith0x3f  阅读(18)  评论(0)    收藏  举报