题目
首先设\(f_{i,j}\)表示在第\(i\)个村庄修了\(j\)个基站的答案。
那么\(f_{i,j}=c_i+\min\limits_{k\in[j-1,i)}(f_{k,j-1}+cost_{k,i})\)。
其中\(cost_{k,i}\)表示只在\(k,i\)修基站的情况下\((k,i)\)中未被覆盖的村庄的\(w\)之和。
我们发现\(j\)可以滚掉,把枚举\(j\)放到外面一层来,就有\(f_i=c_i+\min\limits_{k\in[j-1,i)}(f_k+cost_{k,i})\)。
这东西直接算肯定没法做,我们分析一下每个部分本质上是什么。
首先我们观察\(f_i\)的计算式,\(c_i\)是完全不变的,可以无视掉。
而\(f_k+cost_{k,i}\)这个东西,在固定一个\(i\)时它是一个与\(k\)有关的变量。
那么一个比较自然的想法就是,每次将\(i-1\)扩展至\(i\)时,我们先求出\(f_i\)(在\(i\)位置修一个基站),然后对\(cost\)进行更新(在\(i\)位置不修基站)。
对于一个村庄\(i\),我们先预处理\(st_i,ed_i\),如果在\([st_i,ed_i]\)内没有哪个村庄修了基站的话这个村庄\(i\)就需要被赔偿。
因为\(i\)位置不修基站,所以我们需要处理所有\(ed_p=i\)的\(p\)村庄对\(cost\)的影响。
\(p\)村庄需要被赔偿等价于上一个基站在\([1,st_p-1]\),也就是从\(f_1\sim f_{st_p-1}\)转移过来的需要加上\(w_p\),即把\(cost_{1,i}\sim cost_{st_p-1,i}\)加上\(w_p\)。
那么我们用线段树维护\(f_k+cost_{k,i}\),支持区间加和区间求最小值即可。
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#define pb push_back
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
const int N=20007,inf=0x3f3f3f3f;
using namespace std;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int max(int a,int b){return a>b? a:b;}
int min(int a,int b){return a<b? a:b;}
int d[N],c[N],s[N],w[N],f[N],st[N],ed[N],val[N<<2],tag[N<<2];
vector<int>edp[N];
void pushup(int p){val[p]=min(val[ls],val[rs]);}
void add(int p,int v){val[p]+=v,tag[p]+=v;}
void pushdown(int p){add(ls,tag[p]),add(rs,tag[p]),tag[p]=0;}
void build(int p,int l,int r)
{
tag[p]=0;
if(l==r) return (void)(val[p]=f[l]);
build(ls,l,mid),build(rs,mid+1,r),pushup(p);
}
void update(int p,int l,int r,int L,int R,int v)
{
if(L<=l&&r<=R) return (void)(tag[p]+=v,val[p]+=v);
if(tag[p]) pushdown(p);
if(L<=mid) update(ls,l,mid,L,R,v);
if(R>mid) update(rs,mid+1,r,L,R,v);
pushup(p);
}
int query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return val[p];
if(tag[p]) pushdown(p);
return min((L<=mid? query(ls,l,mid,L,R):inf),(R>mid? query(rs,mid+1,r,L,R):inf));
}
int main()
{
int n=read(),k=read()+1,i,j,sum,ans;
for(i=2;i<=n;++i) d[i]=read();
for(i=1;i<=n;++i) c[i]=read();
for(i=1;i<=n;++i) s[i]=read();
for(i=1;i<=n;++i) w[i]=read();
++n,d[n]=w[n]=inf;
for(i=1;i<=n;++i) st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d,ed[i]=upper_bound(d+1,d+n+1,d[i]+s[i])-d-1,edp[ed[i]].pb(i);
for(i=1,sum=0;i<=n;++i)
{
f[i]=sum+c[i];
for(int p:edp[i]) sum+=w[p];
}
ans=f[n];
for(i=2;i<=k;++i)
{
build(1,1,n);
for(j=1;j<=n;++j)
{
f[j]=(j>i-1? query(1,1,n,i-1,j-1):0)+c[j];
for(int p:edp[j]) if(st[p]>1) update(1,1,n,1,st[p]-1,w[p]);
}
ans=min(ans,f[n]);
}
printf("%d",ans);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11765391.html