P2605 [ZJOI2010]基站选址 线段树优化DP
题意:
分析:
我们发现题目里面每一个建立的基站可能会对之前的状态有所影响,所以我们在设计DP状态时需要将这种影响消除掉
我们设\(f[i][j]\)表示在第\(i\)个村庄修建第\(j\)个基站且不考虑对\([i+1,n]\)个村庄的影响时的最小费用
- 转移方程就是 \(f[i][j]=min(f[k][j-1]+cost[k][i]+c[i])\),其中\(cost[k][i]\)表示在\(i,k\)建立基站后,\([k+1,i-1]\)内没有被覆盖的村庄赔付的和值,这样的DP复杂度是\(O(n^2k)\)
- 我们考虑怎么优化,首先可以滚动数组滚掉一维,\(f[i]=min(f[k]+cost[k][i])+c[i]\)
- 然后我们发现对于\(cost[k][i]\)来说,每个村庄的贡献时独立的,我们记\(lef[i],rig[i]\)表示每一个村庄能接受信号的左右边界,那么对于第\(i\)个村庄安装基站时,从\([1,lef[k]-1]\)转移来的状态必定会赔付村庄\(k\)的费用
- 也就是说我们我们需要一个支持区间修改,区间查询最小值的数据结构,所以我们用线段树维护\(f[k]+cost[k][i]\)(此时\(i\)是在外层枚举的,线段树只要维护\(k\)的信息就可以了),复杂度\(O(nlog_nk)\)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
const int maxn = 2e4+5;
const int inf = 0x3f3f3f3f;
int n,k,ans;
int c[maxn],w[maxn],s[maxn],f[maxn],d[maxn],lef[maxn],rig[maxn],val[maxn<<3],tag[maxn<<3];
vector<int> pos[maxn];
void pushup(int x)
{
val[x]=min(val[x<<1],val[x<<1|1]);
}
void pushdown(int x)
{
if(!tag[x]) return ;
tag[x<<1]+=tag[x];
tag[x<<1|1]+=tag[x];
val[x<<1]+=tag[x];
val[x<<1|1]+=tag[x];
tag[x]=0;
}
void build(int rt,int l,int r)
{
tag[rt]=0;
if(l==r)
{
val[rt]=f[l];
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int ll,int rr,int v)
{
if(l>r) return ;
if(l>=ll&&r<=rr)
{
val[rt]+=v;
tag[rt]+=v;
return ;
}
pushdown(rt);
int mid=(l+r)>>1;
if(ll<=mid) update(rt<<1,l,mid,ll,rr,v);
if(mid<rr) update(rt<<1|1,mid+1,r,ll,rr,v);
pushup(rt);
}
int query(int rt,int l,int r,int ql,int qr)
{
if(ql>qr) return inf;
if(l>=ql&&r<=qr) return val[rt];
pushdown(rt);
int mid=(l+r)>>1;
int res=inf;
if(ql<=mid) res=min(res,query(rt<<1,l,mid,ql,qr));
if(mid<qr) res=min(res,query(rt<<1|1,mid+1,r,ql,qr));
return res;
}
void work()
{
ios::sync_with_stdio(false);
cin>>n>>k;k++;
for(int i=2;i<=n;i++) cin>>d[i];
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1;i<=n;i++) cin>>s[i];
for(int i=1;i<=n;i++) cin>>w[i];
n++;w[n]=d[n]=inf;
for(int i=1;i<=n;i++)
{
lef[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
rig[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
if(d[rig[i]]>d[i]+s[i]) rig[i]--;
pos[rig[i]].push_back(i);
}
for(int i=1;i<=k;i++)
{
if(i==1)
{
int res=0;
for(int j=1;j<=n;j++)
{
f[j]=res+c[j];
for(int l=0;l<pos[j].size();l++) res+=w[pos[j][l]];
}
ans=f[n];
}
else
{
build(1,1,n);
for(int j=1;j<=n;j++)
{
f[j]=query(1,1,n,1,j-1)+c[j];
for(int l=0;l<pos[j].size();l++) if(lef[pos[j][l]]>1) update(1,1,n,1,lef[pos[j][l]]-1,w[pos[j][l]]);
}
ans=min(ans,f[n]);
}
}
cout<<ans<<endl;
}
}
int main()
{
zzc::work();
return 0;
}