Solution
显然
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个村子建了
j
j
j个基站且第
i
i
i个村子建了基站
f
[
i
]
[
j
]
=
c
[
i
]
+
m
i
n
f
[
k
]
[
j
−
1
]
+
c
o
s
t
(
k
,
i
)
f[i][j]=c[i]+min{f[k][j-1]+cost(k,i)}
f[i][j]=c[i]+minf[k][j−1]+cost(k,i)
c
o
s
t
(
k
,
i
)
cost(k,i)
cost(k,i)表示
k
k
k有一个基站,
j
j
j有一个基站,
k
.
.
i
k..i
k..i的补偿代价
关键就是快速计算这个东西了
线段树优化,就是用线段树区间
m
i
n
min
min来
l
o
g
n
logn
logn获得转移来的状态中最小值吧
j
j
j这一维显然可以滚掉
想办法让线段树每个点表示了选这个点作为转移点时的代价
先把
f
[
]
[
j
−
1
]
f[][j-1]
f[][j−1]建树,然后处理
c
o
s
t
cost
cost的问题
当
i
−
−
>
i
+
1
i-->i+1
i−−>i+1时,发现左端点不变,右段点右移了,那么哪些刚好最远i位置可以覆盖到的点就可能要补偿了
所以对于点
x
x
x,通过二分计算
s
t
[
x
]
st[x]
st[x]和
e
d
[
x
]
ed[x]
ed[x]为
x
x
x最左和最右到哪,然后用链表记录
e
d
[
x
]
ed[x]
ed[x]为某个值的点有哪些,
对于
e
d
[
x
]
=
i
ed[x]=i
ed[x]=i的点线段树
[
1
,
s
t
[
x
]
−
1
]
[1,st[x]-1]
[1,st[x]−1]区间加
w
[
x
]
w[x]
w[x],因为
[
s
t
[
x
]
,
i
]
[st[x],i]
[st[x],i]内都可以覆盖到
i
i
i,所以不需要补偿
复杂度
O
(
k
n
l
o
g
n
)
O(knlogn)
O(knlogn),区间加
n
n
n次,区间
m
i
n
min
min也有
n
n
n次
注意:
1.
j
=
=
1
1.j==1
1.j==1的时候
O
(
n
)
O(n)
O(n)特判就行了
2.
n
+
+
,
k
+
+
后
d
[
n
]
=
w
[
n
]
=
I
N
F
,
c
[
n
]
=
0
2.n++,k++后 d[n]=w[n]=INF,c[n]=0
2.n++,k++后d[n]=w[n]=INF,c[n]=0,
f
[
n
]
f[n]
f[n]就是最优解了
Code
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
const int N=20002;
struct node{
int to,ne;
}e[N];
int mn[N<<2],lz[N<<2],h[N],st[N],ed[N],d[N],s[N],w[N],c[N],i,K,n,j,t,tot,f[N],k,ans;
inline char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
int x=0,fl=1;char ch=gc();
for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
return x*fl;
}
void up(int t){mn[t]=min(mn[t<<1],mn[t<<1|1]);}
void down(int t){
if (lz[t]){
mn[t<<1]+=lz[t],mn[t<<1|1]+=lz[t];
lz[t<<1]+=lz[t],lz[t<<1|1]+=lz[t];
lz[t]=0;
}
}
void build(int t,int l,int r){;
lz[t]=0;
if (l==r){
mn[t]=f[l];
return;
}
build(t<<1,l,mid),build(t<<1|1,mid+1,r);
up(t);
}
int query(int t,int l,int r,int x,int y){
if (x>y) return 0;
if (x<=l && r<=y) return mn[t];
down(t);
int ans=2e9;
if (x<=mid) ans=min(ans,query(t<<1,l,mid,x,y));
if (mid<y) ans=min(ans,query(t<<1|1,mid+1,r,x,y));
return ans;
}
void add(int t,int l,int r,int x,int y,int v){
if (x>y) return;
if (x<=l && r<=y){
mn[t]+=v,lz[t]+=v;
return;
}
down(t);
if (x<=mid) add(t<<1,l,mid,x,y,v);
if (mid<y) add(t<<1|1,mid+1,r,x,y,v);
up(t);
}
int main(){
n=rd(),K=rd();
for (i=2;i<=n;i++) d[i]=rd();
for (i=1;i<=n;i++) c[i]=rd();
for (i=1;i<=n;i++) s[i]=rd();
for (i=1;i<=n;i++) w[i]=rd();
n++,K++;
d[n]=w[n]=1e9+5;
for (i=1;i<=n;i++){
st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
ed[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
if (d[ed[i]]-d[i]>s[i]) ed[i]--;//
e[++tot]=(node){i,h[ed[i]]},h[ed[i]]=tot;
}
for (i=1;i<=n;i++){
f[i]=t+c[i];
for (k=h[i];k;k=e[k].ne) t+=w[e[k].to];
}
ans=2e9;
for (j=2;j<=K;j++){
build(1,1,n);
for (i=1;i<=n;i++){
f[i]=query(1,1,n,1,i-1)+c[i];
for (k=h[i];k;k=e[k].ne) add(1,1,n,1,st[e[k].to]-1,w[e[k].to]);
}
ans=min(ans,f[n]);
}
printf("%d",ans);
}