bzoj1835: [ZJOI2010]base 基站选址
Description
有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。
Input
输出文件中仅包含一个整数,表示最小的总费用。
Output
3 2 1 2 2 3 2 1 1 0 10 20 30
Sample Input
4
Sample Output
40%的数据中,N<=500;
100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。
HINT
2018.7.7重设时限为20s,未重测!
分析
一道线段树优化Dp好题
首先方程不难写出
f
[
k
+
1
]
[
i
]
=
min
{
f
[
k
]
[
j
]
+
w
(
j
,
i
)
}
+
c
[
i
]
f[k+1][i]=\min \{f[k][j] + w(j,i)\}+c[i]
f[k+1][i]=min{f[k][j]+w(j,i)}+c[i]
其中
f
[
k
]
[
i
]
f[k][i]
f[k][i]表示建了
k
k
k个基站,前
i
i
i村庄,
i
i
i处建基站的最小代价,
w
(
j
,
i
)
w(j,i)
w(j,i)表示
i
,
j
i,j
i,j建立基站的前提下,
j
j
j到
i
i
i的村庄还需要多少抚恤金。
考虑优化这个方程。关键在于如何处理
w
w
w
可以首先简化这个问题,先不考虑后面的基站对村庄的影响。
考虑暴力
我们在扫
i
i
i的时候把每个村庄的抚恤金暴力加入每个
f
[
k
]
[
j
]
f[k][j]
f[k][j]上。
这个时候注意到每一个村庄的抚恤金,只会对不在其范围内的
f
[
k
]
[
j
]
f[k][j]
f[k][j]产生贡献。
也就是说,每个村庄对
f
[
k
]
f[k]
f[k]产生的贡献都是一段区间。
这样子的话把之前的
f
[
k
]
f[k]
f[k]加入线段树,这样子的话可以把每个村庄的贡献转化成区间加,当前答案转化成区间最小值询问。
这个时候再考虑后面的基站对村庄的影响。
如果当前做到的
i
i
i还在村庄的范围之内的话,这个村庄的抚恤金不需要加入线段树中。
而每次把
i
i
i挪到
i
+
1
i + 1
i+1都可能有若干个村庄要产生贡献。
这个过程可以使用堆来维护。具体地,把所有村庄按照范围右端点插入一个小根堆,一旦某个
i
i
i比堆顶大,那么把这个村庄弹出堆顶加入其贡献即可。
复杂度
O
(
k
n
l
o
g
n
)
O(knlogn)
O(knlogn)
非常优秀的一道题。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
const int Nt = 32767, N = 2e4 + 10, inf = 0x3f3f3f3f;
int ri() {
char c = getchar(); int x = 0; for(;c < '0' || c > '9'; c = getchar()) ;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x;
}
int n, K, id[N], g[N], d[N], c[N], s[N], w[N], L[N], R[N], f[101][N], mn[N << 2], t[N << 2];
struct P {int u, x;}T[Nt << 1];
P min(P a, P b) {return a.x < b.x ? a : b;}
void Up(int i, int x) {for(T[i += Nt].x = x; i >>= 1; T[i] = min(T[i << 1], T[i << 1 | 1])) ;}
void In(int *f, int s = 1) {for(int i = s;i <= n; ++i) f[i] = ri();}
bool cmpr(int a, int b) {return d[a] + s[a] <= d[b] + s[b];}
bool cmpl(int a, int b) {return d[a] - s[a] <= d[b] - s[b];}
void Build(int p, int L, int R, int k) {
t[p] = 0;
if(L == R) return void(mn[p] = f[k][L]);
int m = L + R >> 1; Build(p << 1, L, m, k); Build(p << 1 | 1, m + 1, R, k);
mn[p] = std::min(mn[p << 1], mn[p << 1 | 1]);
}
void Tag(int p, int v) {mn[p] += v; t[p] += v;}
void Push(int p) {if(t[p]) Tag(p << 1, t[p]), Tag(p << 1 | 1, t[p]), t[p] = 0;}
void Add(int p, int L, int R, int st, int ed, int v) {
if(L == st && ed == R) return Tag(p, v);
if(t[p]) Push(p);
int m = L + R >> 1;
if(st <= m) Add(p << 1, L, m, st, std::min(m, ed), v);
if(ed > m) Add(p << 1 | 1, m + 1, R, std::max(st, m + 1), ed, v);
mn[p] = std::min(mn[p << 1], mn[p << 1 | 1]);
}
int Que(int p, int L, int R, int st, int ed) {
if(L == st && ed == R) return mn[p];
if(t[p]) Push(p);
int m = L + R >> 1, r = inf;
if(st <= m) r = std::min(r, Que(p << 1, L, m, st, std::min(m, ed)));
if(ed > m) r = std::min(r, Que(p << 1 | 1, m + 1, R, std::max(st, m + 1), ed));
return r;
}
int main() {
n = ri(); K = ri();
In(d, 2); In(c); In(s); In(w); int ans = 0;
for(int i = 1;i <= n; ++i) id[i] = i, ans += w[i];
std::sort(id + 1, id + n + 1, cmpl);
int t = n;
for(int i = n; i; L[id[i--]] = t)
for(int l = d[id[i]] - s[id[i]]; d[t - 1] >= l && t > 1; --t) ;
std::sort(id + 1, id + n + 1, cmpr);
t = 1;
for(int i = 1;i <= n; R[id[i++]] = t)
for(int r = d[id[i]] + s[id[i]];d[t + 1] <= r && t < n; ++t) ;
std::memset(T, 0x3f, sizeof(T));
for(int i = 1;i <= n; ++i) T[i + Nt].u = i;
for(int i = 1, r = 0;i <= n; ++i) {
for(;T[1].x < i;) r += w[T[1].u], Up(T[1].u, inf);
g[i] = f[1][i] = r + c[i];
Up(i, R[i]);
}
for(int k = 1; k < K; ++k) {
for(;T[1].x != inf;) Up(T[1].u, inf);
Build(1, 1, n, k);
for(int i = k + 1;i <= n; ++i) {
for(;T[1].x < i;) {
if(L[T[1].u] - 1) Add(1, 1, n, 1, L[T[1].u] - 1, w[T[1].u]);
Up(T[1].u, inf);
}
f[k + 1][i] = Que(1, 1, n, k, i - 1) + c[i];
Up(i, R[i]); g[i] = std::min(g[i], f[k + 1][i]);
}
}
for(;T[1].x != inf;) Up(T[1].u, inf);
for(int i = n, r = 0; i; --i) {
for(;-T[1].x > i;) r += w[T[1].u], Up(T[1].u, inf);
ans = std::min(ans, g[i] + r);
Up(i, -L[i]);
}
printf("%d\n", ans);
return 0;
}