bzoj 1835: [ZJOI2010]基站选址

Description

有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。
Input

输出文件中仅包含一个整数,表示最小的总费用。
Output

3 2 1 2 2 3 2 1 1 0 10 20 30
Sample Input
4
Sample Output
40%的数据中,N<=500;
100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000

题解:

这题非常的有意思,完全虐翻了我,开始写的是没有优化的DP,T到40,原来正解就是这个DP的优化,原DP中,定义的是 f[i][j] 表示第j个站安放在i这个位置的最小费用,这里并没有考虑后面的基站,所以我们要新建一个点,且这个点距离很远,费用为0,这样就可以完美的合并答案到这个点上面了,然后转移就是 f[i][j]=f[k][j-1]+c[k][i] ,c[k][i]表示k-i间覆盖不到的点的w总和.

考虑优化:

难点在于求出c[k][i]这个东西,我们就考虑消掉这个东西,再思考会发现,j这一维是可以滚动的,我们就可以考虑用线段树维护f[k][j-1],然后就是维护c这个东西,我们设st[i]为i左边能覆盖到i的最远点,同理ed[i]为右边最远点,那么如果扫到了i,那么ed在i上面的点,并且如果转移是从st-1转移来的,那么这个点就覆盖不到,就要在线段树[1,st[i]-1]的位置加上w[i],这样k的位置就变成了f[k]+w[i]了,所以转移就是查询线段树[1,i-1]中的最小值

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define ls (node<<1)
#define rs (node<<1|1)
using namespace std;
typedef long long ll;
const int N=20005,M=105,inf=2e8;
int gi(){
	int str=0;char ch=getchar();
	while(ch>'9' || ch<'0')ch=getchar();
	while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
	return str;
}
int n,m;int f[N],c[N],w[N],st[N],ed[N],Tree[N<<2],mark[N<<2];ll d[N],s[N];
int midl(int sta,ll x){
	int l=1,r=n,mid,ret=sta;
	while(l<=r){
		mid=(l+r)>>1;
		if(d[mid]>=x)ret=mid,r=mid-1;
		else l=mid+1;
	}
	return ret;
}
int midr(int sta,ll x){
	int l=1,r=n,mid,ret=sta;
	while(l<=r){
 		mid=(l+r)>>1;
	  if(d[mid]<=x)ret=mid,l=mid+1;
		else r=mid-1;
	}
	return ret;
}
int head[N],to[N],num=0,nxt[N];
void addedge(int x,int y){
	nxt[++num]=head[x];to[num]=y;head[x]=num;
}
void upd(int node){
	Tree[node]=Min(Tree[ls],Tree[rs]);
}
void build(int l,int r,int node){
	mark[node]=0;
	if(l==r){
		Tree[node]=f[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,ls);build(mid+1,r,rs);
	upd(node);
}
void pushdown(int node){
	if(!mark[node])return ;
	int k=mark[node];
	Tree[ls]+=k;Tree[rs]+=k;
	mark[ls]+=k;mark[rs]+=k;
	mark[node]=0;
}
void updata(int l,int r,int node,int sa,int se,int to){
	if(l>se || r<sa)return ;
	if(sa<=l && r<=se){
		Tree[node]+=to;mark[node]+=to;
		return ;
	}
	pushdown(node);
	int mid=(l+r)>>1;
	updata(l,mid,ls,sa,se,to);updata(mid+1,r,rs,sa,se,to);
	upd(node);
}
int query(int l,int r,int node,int sa,int se){
	if(l>se || r<sa)return inf;
	if(sa<=l && r<=se)return Tree[node];
	pushdown(node);
	int mid=(l+r)>>1;
	int q1=query(l,mid,ls,sa,se),q2=query(mid+1,r,rs,sa,se);
	upd(node);
	return Min(q1,q2);
}
void work()
{
	n=gi();m=gi();
	for(int i=2;i<=n;i++)d[i]=gi();
	for(int i=1;i<=n;i++)c[i]=gi();
	for(int i=1;i<=n;i++)s[i]=gi();
	for(int i=1;i<=n;i++)w[i]=gi();
	n++;d[n]=2e12;
	for(int i=1;i<=n;i++){
		st[i]=midl(i,d[i]-s[i]);ed[i]=midr(i,d[i]+s[i]);
		addedge(ed[i],i);
	}
	ll tot=0;
	for(int i=1;i<=n;i++){
		f[i]=tot+c[i];
		for(int j=head[i];j;j=nxt[j]){
			tot+=w[to[j]];
		}
	}
	ll ans=f[n];
	for(int j=2;j<=m+1;j++){
		build(1,n,1);
		for(int i=1;i<=n;i++){
			if(i>1)f[i]=query(1,n,1,1,i-1)+c[i];
			for(int k=head[i];k;k=nxt[k]){
				int u=to[k];
				if(st[u]>1)updata(1,n,1,1,st[u]-1,w[u]);
			}
		}
		ans=Min(ans,f[n]);
	}
	printf("%lld\n",ans);
}

int main()
{
	freopen("base.in","r",stdin);
	freopen("base.out","w",stdout);
	work();
	return 0;
}
posted @ 2017-08-25 15:38  PIPIBoss  阅读(156)  评论(0编辑  收藏  举报