P2605 [ZJOI2010]基站选址
题目描述
Solution
首先不难想到一个 的DP。
表示前个村庄选择了个基站的总费用。
考虑如何优化这个转移。
对于村庄,我们记录它覆盖范围内最靠前的村庄 和最靠后的村庄 。
倘若在之后的村庄建立基站,则若上一个基站在之前,那么就会多一个的费用。
增加费用的状态是形如 的连续一段状态。
我们只需要让滚动,维护区间加,询问区间最小值,线段树维护即可。
Ps:luogu题解中很多代码求 时使用的是 再特判减1,但这样如果出现相等的值时,会找到相等的值中第一个值,与 的实际意义不符,但由于我太蒟,不知道会不会对答案产生影响qwq。
Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+50;
const int INF=0x3f3f3f3f;
int d[MAXN],a[MAXN],s[MAXN],w[MAXN],fi[MAXN],la[MAXN],f[MAXN];
vector<int> e[MAXN];
inline int read()
{
int x=0,f=1; char c=getchar();
while (c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
struct Segment_Tree
{
struct segnode{int l,r,tag,mn; } tree[MAXN<<2];
void up(int x){ tree[x].mn=min(tree[x<<1].mn,tree[x<<1|1].mn); }
void down(int x)
{
if (tree[x].tag!=0)
{
int q=tree[x].tag;
tree[x<<1].tag+=q,tree[x<<1].mn+=q;
tree[x<<1|1].tag+=q,tree[x<<1|1].mn+=q;
tree[x].tag=0;
}
}
void build(int x,int l,int r)
{
tree[x].tag=tree[x].mn=0;
if ((tree[x].l=l)==(tree[x].r=r))
{
tree[x].mn=f[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
up(x);
}
int query(int x,int l,int r)
{
if (tree[x].l>=l&&tree[x].r<=r) return tree[x].mn;
down(x);
int mid=(tree[x].l+tree[x].r)>>1;
if (r<=mid) return query(x<<1,l,r);
else if (l>mid) return query(x<<1|1,l,r);
else return min(query(x<<1,l,mid),query(x<<1|1,mid+1,r));
}
void change(int x,int l,int r,int y)
{
if (tree[x].l>=l&&tree[x].r<=r)
{
tree[x].mn+=y;
tree[x].tag+=y;
return;
}
down(x);
int mid=(tree[x].l+tree[x].r)>>1;
if (r<=mid) change(x<<1,l,r,y);
else if (l>mid) change(x<<1|1,l,r,y);
else change(x<<1,l,mid,y),change(x<<1|1,mid+1,r,y);
up(x);
}
} segment;
int main()
{
int n=read(),m=read();
for (int i=2;i<=n;i++) d[i]=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<=n;i++) s[i]=read();
for (int i=1;i<=n;i++) w[i]=read();
n++;
d[n]=w[n]=INF;
for (int i=1;i<=n;i++)
{
fi[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
la[i]=upper_bound(d+1,d+n+1,d[i]+s[i])-d-1;
//cout<<i<<" "<<fi[i]<<" "<<la[i]<<endl;
e[la[i]].push_back(i);
}
int cnt=0,ans=0;
for (int i=1;i<=n;i++)
{
f[i]=cnt+a[i];
for (int k=0;k<e[i].size();k++) cnt+=w[e[i][k]];
}
ans=f[n];
//for (int i=1;i<=n;i++) cout<<i<<" "<<f[i]<<endl;cout<<endl;
for (int j=2;j<=m+1;j++)
{
segment.build(1,1,n);
for (int i=1;i<=n;i++)
{
if (j>i) f[i]=a[i];
else f[i]=segment.query(1,j-1,i-1)+a[i];
for (int k=0;k<e[i].size();k++)
if (fi[e[i][k]]>1) segment.change(1,1,fi[e[i][k]]-1,w[e[i][k]]);
}
ans=min(ans,f[n]);
}
printf("%d\n",ans);
return 0;
}