原题传送门
先是暴力
表示第
个村庄,当前建基站,总共建了
个基站
把枚举基站个数的循环拎到最外面,这样
可以用线段树把枚举
的
优化成
对于每个点求出
表示在这个点建立基站能覆盖到的区间
线段树维护
的值
对于一个点
,需要在
区间内加上
,表示之后的点需要花费
的钱
Code:
#include <bits/stdc++.h>
#define maxn 20010
#define ls rt << 1
#define rs rt << 1 | 1
using namespace std;
struct Seg{
int l, r, sum, tag;
}seg[maxn << 2];
struct Edge{
int to, next;
}edge[maxn << 1];
int num, head[maxn], n, k, d[maxn], s[maxn], c[maxn], w[maxn], dp[maxn], st[maxn], ed[maxn];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
void addedge(int x, int y){ edge[++num] = (Edge){y, head[x]}, head[x] = num; }
int find(int x){
int l = 1, r = n, ans = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (d[mid] >= x) ans = mid, r = mid - 1; else l = mid + 1;
}
return ans;
}
int find2(int x){
int l = 1, r = n, ans = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (d[mid] <= x) ans = mid, l = mid + 1; else r = mid - 1;
}
return ans;
}
void pushup(int rt){ seg[rt].sum = min(seg[ls].sum, seg[rs].sum); }
void pushdown(int rt){
seg[ls].sum += seg[rt].tag, seg[rs].sum += seg[rt].tag;
seg[ls].tag += seg[rt].tag, seg[rs].tag += seg[rt].tag;
seg[rt].tag = 0;
}
void build(int rt, int l, int r){
seg[rt].tag = 0, seg[rt].l = l, seg[rt].r = r;
if (l == r){ seg[rt].sum = dp[l]; return; }
int mid = (l + r) >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
pushup(rt);
}
void update(int rt, int l, int r, int k){
if (seg[rt].l > r || seg[rt].r < l) return;
if (seg[rt].l >= l && seg[rt].r <= r){
seg[rt].sum += k, seg[rt].tag += k;
return;
}
pushdown(rt);
update(ls, l, r, k), update(rs, l, r, k);
pushup(rt);
}
int query(int rt, int l, int r){
if (seg[rt].l > r || seg[rt].r < l) return 1e9;
if (seg[rt].l >= l && seg[rt].r <= r) return seg[rt].sum;
pushdown(rt);
return min(query(ls, l, r), query(rs, l, r));
}
int main(){
n = read(), k = read() + 1;
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();
d[++n] = 1e9, w[n] = 1e9;
for (int i = 1; i <= n; ++i){
if (d[i] - s[i] <= 0) st[i] = 1;
else st[i] = find(d[i] - s[i]);
if (d[i] + s[i] >= d[n]) ed[i] = n;
else ed[i] = find2(d[i] + s[i]);
addedge(ed[i], i);
}
int ans = 1e9;
for (int j = 1; j <= k; ++j){
if (j == 1){
int x = 0;
for (int i = 1; i <= n; ++i){
dp[i] = x + c[i];
for (int u = head[i]; u; u = edge[u].next){
int v = edge[u].to;
x += w[v];
}
}
ans = dp[n];
} else{
build(1, 1, n);
for (int i = 1; i <= n; ++i){
dp[i] = (i >= j ? query(1, j - 1, i - 1) : 0) + c[i];
for (int u = head[i]; u; u = edge[u].next){
int v = edge[u].to;
if (st[v] > 1) update(1, 1, st[v] - 1, w[v]);
}
}
ans = min(ans, dp[n]);
}
}
printf("%d\n", ans);
return 0;
}