[BZOJ1835][ZJOI2010]base 基站选址(DP+线段树)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ1835][ZJOI2010]base 基站选址(DP+线段树)相关的知识,希望对你有一定的参考价值。
首先想到DP,f[i][j]表示前i个村庄,共建了j个站的最小费用,且第j个站建在第i个村庄上
f[i][j]=min(f[i][j],f[k][j-1]+cost(k,i));(1<=k<i)
cost(k,i)表示选了k和i之后,他们之间需要的w的和
然后这样是O(kn^2)的,对于100%的数据会T。我们可以发现瓶颈在于找到min(f[k][j-1]+cost(k,i)),考虑如何优化它。还有显然的是可以舍掉第二维,只要先枚举建的站的数量即可。
当i变为i+1时,对于那些原来能建立了i而被覆盖,现在却不能被覆盖的点,就需要k来覆盖它。我们定义L[i],R[i],分别表示i最左边能覆盖i的点,和最右边的。于是每次对于R[x]=i的点,1~L[x]-1的点的cost都要加上w[x]。于是我们可以用一个线段树维护min(f[k][j-1]+cost(k,i)),求L[i]和R[i]二分就好。
注意一个细节,因为我们并不知道最后一个站建在哪,所以可以手动加一个点,令他的w=inf,c=0,d=inf,s=0,这样就一定会选它了。
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<iostream> #include<string> #include<ctime> #include<queue> #include<map> #include<set> #include<vector> typedef long long LL; using namespace std; const int N=200010,inf=1e9; struct node{int l,r,v,lazy;}a[N<<4]; int n,m,d[N],w[N],s[N],c[N],f[N],L[N],R[N]; vector<int>vec[N]; inline int read() {int d=0,f=1; char c=getchar(); while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1; c=getchar();} while (c>=‘0‘&&c<=‘9‘) d=(d<<3)+(d<<1)+c-48,c=getchar(); return d*f;} void judge(){freopen(".in","r",stdin); freopen(".out","w",stdout);} inline void pushup(int k){a[k].v=min(a[k<<1].v,a[k<<1|1].v);} inline void pushdown(int k) { if (!a[k].lazy) return; int k1=k<<1,k2=k1|1; a[k1].lazy+=a[k].lazy; a[k2].lazy+=a[k].lazy; a[k1].v+=a[k].lazy; a[k2].v+=a[k].lazy; a[k].lazy=0; } inline void build(int k,int l,int r) { a[k]=(node){l,r,0,0}; if (l==r) {a[k].v=f[l]; return;} int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); pushup(k); } inline void update(int k,int l,int r,int v) { if (l>r) return; if (l<=a[k].l&&a[k].r<=r) {a[k].v+=v; a[k].lazy+=v; return;} pushdown(k); int mid=(a[k].l+a[k].r)>>1; if (r<=mid) update(k<<1,l,r,v); else if (l>mid) update(k<<1|1,l,r,v); else update(k<<1,l,mid,v),update(k<<1|1,mid+1,r,v); pushup(k); } inline int query(int k,int l,int r) { if (l>r) return 0; if (l<=a[k].l&&a[k].r<=r) return a[k].v; pushdown(k); int mid=(a[k].l+a[k].r)>>1; if (r<=mid) return query(k<<1,l,r); else if (l>mid) return query(k<<1|1,l,r); else return min(query(k<<1,l,mid),query(k<<1|1,mid+1,r)); } int main() { //judge(); n=read(); m=read(); for (int i=2;i<=n;i++) d[i]=read(); for (int i=1;i<=n;i++) c[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]=inf; c[n]=0; s[n]=0; w[n]=inf; for (int i=1;i<=n;i++) { L[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d; R[i]=lower_bound(d+1,d+1+n,d[i]+s[i])-d; if (d[R[i]]>d[i]+s[i]) R[i]--; vec[R[i]].push_back(i); } int ljj=0; for (int i=1;i<=n;i++) { f[i]=ljj+c[i]; for (int j=0;j<vec[i].size();j++) ljj+=w[vec[i][j]]; } int ans=f[n]; for (ljj=1;ljj<=m;ljj++) { build(1,1,n); for (int i=1;i<=n;i++) { f[i]=query(1,1,i-1)+c[i]; for (int j=0;j<vec[i].size();j++) { int k=vec[i][j]; update(1,1,L[k]-1,w[k]); } } ans=min(ans,f[n]); } printf("%d",ans); return 0; }
话说stl是真方便!
注:ZJOI2009的题好鬼畜啊,不想做了,07,08的不写题解了,可以看小乐乐的博客:http://blog.163.com/eden_284914869/
以上是关于[BZOJ1835][ZJOI2010]base 基站选址(DP+线段树)的主要内容,如果未能解决你的问题,请参考以下文章