[ZJOI 2010]base 基站选址

Posted NaVi_Awson

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI 2010]base 基站选址相关的知识,希望对你有一定的参考价值。

Description

题库链接

给出 \(n\) 个村庄的横坐标 \(D_i\) 。要求在这 \(n\) 个村庄内最多选择 \(m\) 个作为通讯基站,在村庄 \(i\) 建造通讯基站的代价为 \(C_i\) 。对于村庄 \(i\) ,如果其左右距离超过 \(S_i\) 都没有通讯基站,那么需要额外的 \(W_i\) 的代价。求最小代价。

\(1\leq n\leq 20000,1\leq m\leq 100\)

Solution

\(f_{i,j}\) 表示在第 \(i\) 个村庄修建第 \(j\) 个基站的最小费用。

可以处理出一个数组 \(cost_{k,i}\) 表示第 \(i\sim k\) 个村庄之间没有被基站 \(i,k\) 覆盖的村庄所需的额外费用。

那么转移方程 \(f_{i,j} = \min\limits_{k<i} f_{k,j-1}+cost_{k,i}+C_i\) 。转移复杂度为 \(O(n^2m)\)

考虑优化。

首先我们可以先把 \(j\) 给滚掉。其次我们注意到每次选择时都是在 \(k\in [1,i)\) 中取一个 \(f_k+cost_{k,i}\) 的最小值。可以用线段树优化查询最小值。

但剩下的就是如何处理 \(cost\) 的更新。

对于一个村庄 \(i\) ,可以二分处理出它所能被覆盖的左右边界为 \(l_i,r_i\),然后在用邻接表记录 \(r\) 值为 \(i\) 的村庄有哪些,在这些村庄之后建立基站就覆盖不到 \(i\) 了。

这样当我们从 \(i\) 推到 \(i + 1\) 时,对于所有 \(r_k=i\) 的村庄若从村庄 \(1\sim l_k-1\) 转移过来则必定要赔偿村庄 \(k\) 的费用,这样就可以用线段树区间加,即在区间 \([1,l_k)\) 加上村庄 \(k\) 的额外费用。

总复杂度为\(O(nmlog_2n)\)

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 20000, inf = ~0u>>1;

int n, m, d[N+5], c[N+5], s[N+5], w[N+5], l[N+5], r[N+5]; long long f[N+5];
vector<int>to[N+5];
struct Segment_tree {
    #define lr(o) (o<<1)
    #define rr(o) (o<<1|1)
    long long sgm[(N<<2)+5], lazy[(N<<2)+5];
    void build(int o, int l, int r) {
        lazy[o] = 0; int mid = (l+r)>>1;
        if (l == r) {sgm[o] = f[l]; return; }
        build(lr(o), l, mid); build(rr(o), mid+1, r);
        sgm[o] = min(sgm[lr(o)], sgm[rr(o)]);
    }
    void pushdown(int o) {
        sgm[lr(o)] += lazy[o], lazy[lr(o)] += lazy[o];
        sgm[rr(o)] += lazy[o], lazy[rr(o)] += lazy[o];
        lazy[o] = 0;
    }
    void update(int o, int l, int r, int a, int b, long long k) {
        if (a <= l && r <= b) {sgm[o] += k, lazy[o] += k; return; }
        if (lazy[o]) pushdown(o); int mid = (l+r)>>1;
        if (a <= mid) update(lr(o), l, mid, a, b, k);
        if (b > mid) update(rr(o), mid+1, r, a, b, k);
        sgm[o] = min(sgm[lr(o)], sgm[rr(o)]);
    }
    long long query(int o, int l, int r, int a, int b) {
        if (a <= l && r <= b) return sgm[o];
        if (lazy[o]) pushdown(o); int mid = (l+r)>>1;
        long long c1 = inf, c2 = inf;
        if (a <= mid) c1 = query(lr(o), l, mid, a, b);
        if (b > mid) c2 = query(rr(o), mid+1, r, a, b);
        return min(c1, c2);
    }
}T;

void work() {
    scanf("%d%d", &n, &m);
    for (int i = 2; i <= n; i++) scanf("%d", &d[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &s[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
    ++m; ++n; d[n] = w[n] = inf;
    for (int i = 1; i <= n; i++) {
        r[i] = lower_bound(d+1, d+n+1, d[i]+s[i])-d;
        l[i] = lower_bound(d+1, d+n+1, d[i]-s[i])-d;
        if (d[r[i]]-d[i] > s[i]) --r[i];
        to[r[i]].push_back(i);
    }
    long long tol = 0;
    for (int i = 1; i <= n; i++) {
        f[i] = c[i]+tol;
        for (int j = 0, sz = to[i].size(); j < sz; j++)
            tol += w[to[i][j]];
    }
    long long ans = f[n];
    for (int i = 2; i <= m; i++) {
        T.build(1, 1, n);
        for (int j = 1; j <= n; j++) {
            f[j] = (j != 1 ? T.query(1, 1, n, 1, j-1) : 0)+c[j];
            for (int k = 0, sz = to[j].size(); k < sz; k++)
                if (l[to[j][k]] != 1) T.update(1, 1, n, 1, l[to[j][k]]-1, w[to[j][k]]);
        }
        ans = min(ans, f[n]);
    }
    printf("%lld\n", ans);
}
int main() {work(); return 0; }

以上是关于[ZJOI 2010]base 基站选址的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)

bzoj1835[ZJOI2010]base基站选址

BZOJ 1835 [ZJOI2010]base 基站选址

bzoj1835: [ZJOI2010]base 基站选址

bzoj1835 [ZJOI2010]基站选址

[BZOJ1835][ZJOI2010]base 基站选址(DP+线段树)