\(\text{Solution}\)
一道比较简单线段树优化\(DP\),但它是黑色的!!!
一个显然的\(DP\),设\(f_{i,j}\)表示选到第\(i\)个(\(i\)必选),建了\(j\)个基站。
\[f_{i,j} = f_{k,j - 1} + Sw_{k,i} + C_i
\]
\(Sw_{i,j}\)表示\(i\)到\(j\)中没被覆盖的村庄的补偿总费用
这样\(DP\)是\(O(n^2)\)
发现对于一个村庄\(x\),如果转移的\(j\)不在区间\([x - S_x,x + S_x]\)内就会加\(W_x\),那么就可以用线段树维护\(DP\)值,对区间\([1,x - S_x - 1]\)的数加上\(W_x\)即可。
最后的答案在随便处理一下即可。
\(\text{Code}\)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 2e4 + 5,inf = 1e9;
int n,m,d[N],c[N],w[N],f[N << 2],tag[N << 2],g[N][105];
struct nd{int l,r,id;}a[N];
bool cmp(nd x,nd y){return x.r < y.r;}
void build(int l,int r,int k,int x)
{
tag[k] = 0;
if (l == r) return f[k] = g[l][x],void();
int mid = l + r >> 1;
build(l,mid,k << 1,x),build(mid + 1,r,k << 1 | 1,x);
f[k] = min(f[k << 1],f[k << 1 | 1]);
}
void pushdown(int k)
{
if (!tag[k]) return;
tag[k << 1] += tag[k],tag[k << 1 | 1] += tag[k];
f[k << 1] += tag[k],f[k << 1 | 1] += tag[k],tag[k] = 0;
}
void update(int l,int r,int k,int L,int R,int v)
{
if (L > R || l > R || r < L || !v) return;
if (L <= l && r <= R) return f[k] += v,tag[k] += v,void();
pushdown(k); int mid = l + r >> 1;
if (L <= mid) update(l,mid,k << 1,L,R,v);
if (R > mid) update(mid + 1,r,k << 1 | 1,L,R,v);
f[k] = min(f[k << 1],f[k << 1 | 1]);
}
int query(int l,int r,int k,int L,int R)
{
if (L <= l && r <= R) return f[k];
pushdown(k); int mid = l + r >> 1,tmp = inf;
if (L <= mid) tmp = query(l,mid,k << 1,L,R);
if (R > mid) tmp = min(tmp,query(mid + 1,r,k << 1 | 1,L,R));
return tmp;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 2; i <= n; i++) scanf("%d",&d[i]);
for (int i = 1; i <= n; i++) scanf("%d",&c[i]);
for (int i = 1,s; i <= n; i++)
{
scanf("%d",&s),a[i] = nd{d[i] - s,d[i] + s,i};
int l = 1,r = n,res = 1;
while (l <= r) {
int mid = l + r >> 1;
if (d[mid] >= a[i].l) res = mid,r = mid - 1;
else l = mid + 1;
}a[i].l = res,l = 1,r = n,res = n;
while (l <= r) {
int mid = l + r >> 1;
if (d[mid] <= a[i].r) res = mid,l = mid + 1;
else r = mid - 1;
}a[i].r = res;
}
for (int i = 1; i <= n; i++) scanf("%d",&w[i]);
n++,a[n] = nd{n,n,n};
sort(a + 1,a + 1 + n,cmp); int ans = inf;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= m; j++) g[i][j] = inf;
g[0][0] = 0;
for (int j = 1; j <= m + 1; j++)
{
build(0,n,1,j - 1);
for (int i = 1,k = 1; i <= n; i++)
{
while (k <= n && a[k].r < i) update(0,n,1,0,a[k].l - 1,w[a[k].id]),k++;
g[i][j] = query(0,n,1,0,i - 1) + c[i];
}
}
for (int i = 1; i <= m + 1; i++) ans = min(ans,g[n][i]);
printf("%d\n",ans);
}