bzoj 1835 base 基站选址 - 动态规划 - 线段树

时间:2021-11-28 12:47:17

题目传送门

  需要高级权限的传送门

题目大意

  有$n$个村庄坐落在一条直线上,第$i \ \ \ (i>1)$个村庄距离第$1$个村庄的距离为$D_i$。需要在这些村庄中建立不超过$K$个通讯基站,在第$i$个村庄建立基站的费用为$C_i$。如果在距离第$i$个村庄不超过$S_i$的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第$i$个村庄没有被覆盖,则需要向他们补偿,费用为$W_i$。现在的问题是,选择基站的位置,使得总费用最小。

  三方dp是显然的,用$f_{i, j}$表示考虑前$i$个村庄,在第$i$个村庄建立基站的最小费用。

  预处理一下,每个村庄距离不超过$S_i$的村庄区间。然后考虑用某个数据结构来维护转移。

  将这些区间按照右端点排序,当当前考虑的$i$大于这个区间的右端点的时候,那么这个区间的左端点以前的状态的转移需要加上它的赔偿费用。

  然后就做完了。时间复杂度$O(nk\log n)$

Code

 /**
* bzoj
* Problem#1835
* Accepted
* Time: 2468ms
* Memory: 11300k
*/
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>
using namespace std;
typedef bool boolean;
#define ll long long const signed int inf = (signed) (~0u >> );
const int N = 2e4 + , Kmx = ; typedef class Segment {
public:
int l, r, cost; boolean operator < (Segment s) const {
return r < s.r;
}
}Segment; typedef class SegTreeNode {
public:
int val, tg;
SegTreeNode *l, *r; void pushUp() {
val = (l->val < r->val) ? (l->val) : (r->val);
} void pushDown() {
l->val += tg, l->tg += tg;
r->val += tg, r->tg += tg;
tg = ;
}
}SegTreeNode; SegTreeNode pool[N << ];
SegTreeNode *top; SegTreeNode* newnode() {
top->val = inf, top->tg = ;
top->l = top->r = NULL;
return top++;
} typedef class SegTree {
public:
int n;
SegTreeNode* rt; SegTree() { }
SegTree(int n):n(n) {
top = pool;
build(rt, , n);
} void build(SegTreeNode*& p, int l, int r) {
p = newnode();
if (l == r)
return;
int mid = (l + r) >> ;
build(p->l, l, mid);
build(p->r, mid + , r);
} void update(SegTreeNode* p, int l, int r, int ql, int qr, int val) {
if (ql == l && r == qr) {
p->val += val;
p->tg += val;
return;
}
if (p->tg)
p->pushDown();
int mid = (l + r) >> ;
if (qr <= mid)
update(p->l, l, mid, ql, qr, val);
else if (ql > mid)
update(p->r, mid + , r, ql, qr, val);
else {
update(p->l, l, mid, ql, mid, val);
update(p->r, mid + , r, mid + , qr, val);
}
p->pushUp();
} void update(SegTreeNode* p, int l, int r, int idx, int val) {
if (l == r) {
p->val = val;
return;
}
if (p->tg)
p->pushDown();
int mid = (l + r) >> ;
if (idx <= mid)
update(p->l, l, mid, idx, val);
else
update(p->r, mid + , r, idx, val);
p->pushUp();
} int query() {
return rt->val;
} void update(int l, int r, int val) {
if (l > r)
return;
update(rt, , n, l, r, val);
} void update(int p, int val) {
update(rt, , n, p, val);
}
}SegTree; int n, K, tp = ;
int dist[N], cost[N], rang[N];
int comp[N];
int f[Kmx][N];
Segment sgs[N];
SegTree st; inline void init() {
scanf("%d%d", &n, &K);
dist[] = ;
for (int i = ; i <= n; i++)
scanf("%d", dist + i);
for (int i = ; i <= n; i++)
scanf("%d", cost + i);
for (int i = ; i <= n; i++)
scanf("%d", rang + i);
for (int i = ; i <= n; i++)
scanf("%d", comp + i);
} int res = ;
inline void solve() {
for (int i = ; i <= n; i++) {
int l = dist[i] - rang[i], r = dist[i] + rang[i];
sgs[i].l = lower_bound(dist + , dist + i + , l) - dist;
sgs[i].r = upper_bound(dist + i, dist + n + , r) - dist - ;
sgs[i].cost = comp[i];
} sort(sgs + , sgs + n + ); for (int i = ; i <= n; i++)
res += comp[i];
if (!K) {
printf("%d", res);
return;
} int costs = ;
Segment* p = sgs + , *ped = sgs + n + ;
for (int i = ; i <= n; i++) {
f[][i] = costs + cost[i];
while (p != ped && p->r <= i)
costs += p->cost, p++;
} for (int k = ; k <= K; k++) {
st = SegTree(n);
p = sgs + ;
for (int i = ; i <= n; i++) {
f[k + ][i] = st.query() + cost[i];
while (p != ped && p->r <= i)
st.update(, p->l - , p->cost), p++;
st.update(i, f[k][i]);
}
res = min(res, st.query());
}
printf("%d", res);
} int main() {
init();
solve();
return ;
}