zoukankan      html  css  js  c++  java
  • 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
    ",ans);
    }
    
    int main()
    {
    	freopen("base.in","r",stdin);
    	freopen("base.out","w",stdout);
    	work();
    	return 0;
    }
    
  • 相关阅读:
    二十三种设计模式 python实现
    python logging的输出
    redis
    Django1.11序列化与反序列化
    Django1.11基础视图
    Django1.11模型类数据库操作
    Django1.11创建
    泛型全面分析和应用(二)
    泛型全面分析和应用(一)
    注解的基本盘点 -- 《Java编程思想》
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7428330.html
Copyright © 2011-2022 走看看