题解:P2605 [ZJOI2010] 基站选址

笔记列表

  • 首先容易相当一个简单 \(dp\)
  • \(f_{i, j}\) 表示前 \(i\) 个位置,放了 \(j\) 个基站, 在补偿仅考虑前 \(i\) 个位置的最小代价
  • 转移

\[f_{i, j} = min_{1\leq k < i} \{f_{k,j-1}\;+cost(k + 1)\} + c_i \]

  • 这里\(cost(k+1)\) 表示当前在建设 \(k\)\(i\) 为基站时,\([k+1,i-1]\) 中没有被覆盖的补偿和
  • 发现复杂度在于如何维护 \(cost\)
  • \(L_i, R_i\) 表示能够覆盖 \(i\) 的最左和最右基站
  • 发现当满足 \([k+1,i-1]\) 包含 \([L_i, R_i]\) 时,\(i\) 会往 \(cost(k+1)\) 贡献以个 \(w_i\)
  • 从上面这里可以发现,在 \(i\) 这个位置的贡献是,当在转移 \([R_i + 1, n]\) 时,计算 $[1,L_i - 1] $ 的 \(cost\) 会产生贡献
  • 所以可以用线段树动态的维护 \(cost\)
  • 每个位置,表示这个位置的 \(dp\) 值 和 \(cost\) 值的和
  • 也就时当前位置想要转移的代价
  • \(i\) 这个位置转移之后,把所有 \(R = i\) 的限制, 把 \([1,L-1]\) 区间加上点\(w_i\)
  • 因为我们后面的\(i>R\) , 在计算\([1,L-1]\) 的时,一定有这个贡献
  • 然后滚动可以滚一维
  • 初始化成\(f_{i,0}\) 直接计算在\([1,i]\) 不修建的补偿代价
  • 每一次先以上一轮的\(f_i\)值建树
  • 转移时直接查询区间最小值
  • 注意答案不是\(f_{n,k}\) 因为不一定要在 \(n\)
  • 应该是直接在线段树上查找最小值(因为我们线段树上的值已经包括了\(cost\)
  • 有因为不超过 \(k\) 个,也就是基站可以不建完,所以每轮转移完都要取\(ans\) 最小值
$\text{代码}$
#include<bits/stdc++.h>
#define int long long
#define fore(i,a,b) for( int i=(a); i<=(b); ++i)
#define repe(i,a,b) for( int i=(a); i>=(b); --i)
using namespace std;
const int N = 2e4 + 10;
int f[N];
int c[N], s[N], w[N];
int dis[N];
int n, k, ans = 1e18;
vector<pair<int,int> > ud[N]; // l , id

inline int ls(int p) {
    return (p << 1);
}
inline int rs(int p) {
    return ((p << 1) | 1);
}
struct Segtr {
    int lazy[N << 2];
    int t[N << 2];
    void pushup(int p) {
        t[p] = min(t[ls(p)], t[rs(p)]);
    }
    void build(int p,int l,int r) {
        lazy[p] = 0;
        if(l == r) {
            t[p] = f[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(ls(p), l, mid);
        build(rs(p), mid + 1, r);
        pushup(p);
    }
    void updateson(int p,int val) {
        t[p] += val;
        lazy[p] += val;
    }
    void pushdown(int p) {
        if(lazy[p] == 0) return;
        updateson(ls(p), lazy[p]);
        updateson(rs(p), lazy[p]);
        lazy[p] = 0;
    }
    void update(int p,int l,int r,int L,int R,int val) {
        if(L <= l && r <= R) {
            updateson(p, val);
            return;
        }
        pushdown(p);
        int mid = (l + r) >> 1;
        if(L <= mid) update(ls(p), l, mid, L, R, val);
        if(R >= mid + 1) update(rs(p), mid + 1, r, L, R, val);
        pushup(p);
    }
    int query(int p,int l,int r,int L,int R) {
        if(L <= l && r <= R) {
            return t[p];
        }
        int mid = (l + r) >> 1;
        int res = 1e18;
        pushdown(p);
        if(L <= mid) res = min(res, query(ls(p), l, mid, L, R));
        if(R >= mid + 1) res = min(res, query(rs(p), mid + 1, r, L, R));
        return res;
    }
}T;
signed main(){
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	// freopen(".in","r",stdin);
	// freopen(".out","w",stdout);
	cin >> n >> k;
    fore(i, 2, n) cin >> dis[i];
    dis[0] = -1e18;
    dis[n + 1] = 1e18;
    fore(i, 1, n) cin >> c[i];
    fore(i, 1, n) cin >> s[i];
    fore(i, 1, n) cin >> w[i];

    fore(i, 1, n) {
        int L = lower_bound(dis, dis + n + 1, dis[i] - s[i]) - dis;
        int R = upper_bound(dis, dis + n + 1, dis[i] + s[i]) - dis - 1;
        ud[R].push_back({L, i});
    }
    int res = 0;
    fore(i, 1, n) {
        f[i] = res + c[i];
        for(auto x : ud[i]) res += w[x.second];
    }
    ans = min(ans, f[n]);

    fore(J, 1, k) {
        T.build(1, 0, n);
        fore(i, 1, n) {
            f[i] = T.query(1, 0, n, 0, i - 1) + c[i];
            for(auto x : ud[i]) T.update(1, 0, n, 0, x.first - 1, w[x.second]);
        }
        ans = min(ans, T.query(1, 0, n, 0, n));
    }
    cout << ans << '\n';
	return 0;
}

posted @ 2026-09-16 20:39  wmq2012  阅读(4)  评论(0)    收藏  举报