Luogu P2605 [ZJOI2010]基站选址
分析
考虑最基本的DP,设\(f[i][j]\)表示前\(i\)个,建\(j\)个基站,其中第\(j\)个基站在\(i\)上,只考虑\(1->i\)的最小答案
\[ f[i][j]=min(f[k][j-1]+cost(k,i))+co[i],k<i
\]
其中\(cost(k,i)\)表示\([k,i]\)中\(k\)和\(i\)上有基站的补偿费
可以建一个虚点\(n+1\),则最后答案为\(min{f[n+1][k],k \in [1,m+1]}\)
考虑优化\(cost\)
每个点能被覆盖的基站的范围是\([d[i]-s[i],d[i]+s[i]]\),只与i有关
发现当一个节点在右边\(i\)不被覆盖,则也不会被\(i+1,i+2,...\)覆盖
所以可以预处理出每个节点最右边能使其被覆盖的节点,记作\(R[i]\),同时求出每个节点最左边的节点\(L[i]\);
当遍历到\(R[i]\)时,将\([0,L[i]-1]\)的\(f\)加上\(w[i]\)
用线段树维护即可
注意:维护初始值需要维护建立一个基站的情况,以保证性质不被1破坏
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,d[N],co[N],c[N],t[N],f[N],w[N],s[N],L[N],R[N];
vector<int>V[N];
inline int low(int l,int r,int x) {
int mid,ret=l;
while(l<=r) {
int mid=l+r>>1;
if(x<=d[mid]) ret=mid,r=mid-1;
else l=mid+1;
}
return ret;
}
inline int upp(int l,int r,int x) {
int mid,ret=r;
while(l<=r) {
int mid=l+r>>1;
if(x>=d[mid]) ret=mid,l=mid+1;
else r=mid-1;
}
return ret;
}
inline void up(int p) {
c[p]=min(c[p<<1],c[p<<1|1]);
}
void bld(int p,int l,int r) {
c[p]=t[p]=0;
if(l==r) {
c[p]=f[l]; return;
}
int mid=l+r>>1;
bld(p<<1,l,mid),bld(p<<1|1,mid+1,r);
up(p);
}
inline void down(int p,int l,int r,int mid) {
if(t[p]) {
if(l<=mid) c[p<<1]+=t[p],t[p<<1]+=t[p];
if(mid<r) c[p<<1|1]+=t[p],t[p<<1|1]+=t[p];
t[p]=0;
}
}
int ask(int p,int l,int r,int x,int y) {
if(x>y) return 0;
if(l==x&&r==y) {
return c[p];
}
int mid=l+r>>1;
down(p,l,r,mid);
if(y<=mid) return ask(p<<1,l,mid,x,y);
if(x>mid) return ask(p<<1|1,mid+1,r,x,y);
return min(ask(p<<1,l,mid,x,mid),ask(p<<1|1,mid+1,r,mid+1,y));
}
void add(int p,int l,int r,int x,int y,int k) {
if(l==x&&r==y) {
c[p]+=k; t[p]+=k; return;
}
int mid=l+r>>1;
down(p,l,r,mid);
if(y<=mid) add(p<<1,l,mid,x,y,k);
else if(x>mid) add(p<<1|1,mid+1,r,x,y,k);
else {
add(p<<1,l,mid,x,mid,k);
add(p<<1|1,mid+1,r,mid+1,y,k);
}
up(p);
}
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",&co[i]);
for(int i=1;i<=n;i++) scanf("%d",&s[i]);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
n++,m++; d[n]=2e9;
for(int i=1;i<=n;i++) {
L[i]=low(1,i,d[i]-s[i]);
R[i]=upp(i,n,d[i]+s[i]);
V[R[i]].push_back(i);
}
int t=0;
for(int i=1;i<=n;i++) {
f[i]=t+co[i];
for(auto v:V[i])t+=w[v];
}
int ans=f[n];
for(int j=2;j<=m;j++) {
bld(1,0,n);
for(int i=j;i<=n;i++) {
f[i]=ask(1,0,n,j-1,i-1)+co[i];
for(auto v:V[i]) {
add(1,0,n,0,L[v]-1,w[v]);
}
}
ans=min(ans,f[n]);
}
printf("%d\n",ans);
return 0;
}