题解: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;
}

浙公网安备 33010602011771号