P2605 [ZJOI2010]基站选址
题目描述
详见: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;
}