LuoguP2605 [ZJOI2010]基站选址 线段树优化DP
比较好的一道题 DP 题.
令 $f[i][j]$ 表示覆盖前 $i$ 个基站且 $i$ 位置上维修了基站的最小代价.
注意:上面设的状态是不考虑 $[i+1,n]$ 的.
转移的话 $f[i][j] \leftarrow f[k][j-1]+calc(j-1,i)$.
其中 $calc(i,j)$ 表示 $i$ 和 $j$ 分别修了基站的情况下 $i,j$ 之间需要用 $W_{i}$ 的总和.
然后这个 $calc(i,j)$ 可以在枚举 $j$ 的时候在线段树上更新.
即对于每个位置记录其被管辖范围 $[l,r]$ 然后扫描到 $r+1$ 时将 $[0,l-1]$ 做一个区间加法即可.
空间复杂度 $O(n)$,时间复杂度 $O(Kn \log n)$.
code:
#include <cstdio> #include <vector> #include <cstring> #include <algorithm> #define N 20009 #define ll long long #define lson now<<1 #define rson now<<1|1 #define pb push_back #define setIO(s) freopen(s".in","r",stdin) using namespace std; const int inf=1000000009; int n,K; int left[N],right[N],f[N]; int mn[N<<2],lazy[N<<2],dis[N],cost[N],si[N],wi[N]; vector<int>g[N]; inline void pushup(int now) { mn[now]=min(mn[lson],mn[rson]); } inline void mark(int now,int v) { mn[now]+=v,lazy[now]+=v; } inline void pushdown(int now) { if(lazy[now]) { mark(lson,lazy[now]); mark(rson,lazy[now]); lazy[now]=0; } } void build(int l,int r,int now) { lazy[now]=0; if(l==r) { mn[now]=f[l]; return; } int mid=(l+r)>>1; build(l,mid,lson),build(mid+1,r,rson); pushup(now); } int query(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) { return mn[now]; } pushdown(now); int mid=(l+r)>>1,re=inf; if(L<=mid) re=min(re,query(l,mid,lson,L,R)); if(R>mid) re=min(re,query(mid+1,r,rson,L,R)); return re; } void update(int l,int r,int now,int L,int R,int v) { if(l>=L&&r<=R) { mark(now,v); return; } pushdown(now); int mid=(l+r)>>1; if(L<=mid) update(l,mid,lson,L,R,v); if(R>mid) update(mid+1,r,rson,L,R,v); pushup(now); } int main() { // setIO("input"); scanf("%d%d",&n,&K); dis[1]=0; for(int i=2;i<=n;++i) scanf("%d",&dis[i]); for(int i=1;i<=n;++i) scanf("%d",&cost[i]); for(int i=1;i<=n;++i) scanf("%d",&si[i]); for(int i=1;i<=n;++i) scanf("%d",&wi[i]); ++n,++K,cost[n]=0,wi[n]=inf; for(int i=1;i<n;++i) { int l,r,mid,ans; ans=i,l=1,r=i-1; while(l<=r) { mid=(l+r)>>1; if(dis[i]-si[i]<=dis[mid]) { ans=mid; r=mid-1; } else l=mid+1; } left[i]=ans; ans=i,l=i,r=n-1; while(l<=r) { mid=(l+r)>>1; if(dis[i]+si[i]>=dis[mid]) { ans=mid; l=mid+1; } else r=mid-1; } right[i]=ans; g[right[i]].pb(i); } int ans=inf; for(int j=1;j<=K;++j) { if(j==1) { int tot=0; for(int i=1;i<=n;++i) { f[i]=tot+cost[i]; for(int j=0;j<g[i].size();++j) { tot+=wi[g[i][j]]; } } ans=min(ans,f[n]); } else { build(0,n,1); for(int i=1;i<=n;++i) { f[i]=query(0,n,1,0,i-1)+cost[i]; for(int j=0;j<g[i].size();++j) { int p=g[i][j]; update(0,n,1,0,left[p]-1,wi[p]); } } ans=min(ans,f[n]); } } printf("%d\n",ans); return 0; }