博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
P2605 [ZJOI2010]基站选址
阅读量:5052 次
发布时间:2019-06-12

本文共 3603 字,大约阅读时间需要 12 分钟。

题目描述

有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。

输入输出格式

输入格式:

 

输入文件的第一行包含两个整数N,K,含义如上所述。

第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。

第三行包含N个整数,表示C1,C2,…CN。

第四行包含N个整数,表示S1,S2,…,SN。

第五行包含N个整数,表示W1,W2,…,WN。

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 
3 21 22 3 21 1 010 20 30
输出样例#1: 
4

说明

40%的数据中,N<=500;

100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。

 

Solution:

  本题线段树优化dp。

  定义状态$f[i][j]$表示到了$i$村庄建立第$j$个基站的最小费用(不考虑对$i+1\rightarrow n$村庄的影响),则不难得到状态转移方程:$f[i][j]=\min (f[k][j-1]+cost[k][i])+c[i],j-1\leq k<i$,其中$cost[k][i]$表示在$k$村庄和$i$村庄建立基站中间不会被覆盖到的村庄所需补偿费用和。

  对于$cost[k][i]$,我们完全可以预处理出每个村庄建立基站可以覆盖到的边界(二分),然后对补偿费用作前缀和,就能在转移时做到$O(1)$求$cost[k][i]$了,但是这样时间复杂度还是下不去的$O(kn^2)$。

  我们还是预处理出$st[i],ed[i]$分别表示$i$村庄建基站往左最多覆盖到的村庄和往右最多覆盖到的村庄,再用链表建立覆盖关系$ed[i]\rightarrow i$。

  状态转移时,我们可以先滚掉后面一维。然后发现每次村庄右移引起的不会被覆盖的点最多只会比上个位置不会被覆盖的点多$1$,具体来说,当我们由$f[k]$转移到状态$f[i+1]$时,若$ed[p]=i$且$k\in [1,st[p])$则转移时一定会加上覆盖$p$的费用(不在范围内的$k$位置转移的值并没有改变),那么这个区间修改区间最值的转移问题,想到用线段树来优化。

  设当前需要转移的是第$j$个基站,线段树维护$j-1$状态在每个村庄建立基站的最小费用,由某一状态转移到$i$时只需要查询$[j-1,i-1]$的最小值就好了(注意越界问题,至少要有$j-1$个基站),每次转移完就扫一下以$i$点为$ed$的节点$p$,找到$st[p]$,对$[1,st[p]-1]$这段区间的状态整体加上$w[p]$。

  在实现时由于还得知道最后的答案,我们可以在末尾放置一个哨兵村庄,将其设置为必须被选且覆盖不到任何村庄,然后令村庄数$+1$、基站数$+1$,那么每个$f[n]$就是当前状态下的答案了,取每个状态的最小值就好了。

代码:

/*Code by 520 -- 9.25*/#include
#define il inline#define ll long long#define RE register#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;const int N=100005;int n,k,ans,st[N],ed[N];int d[N],c[N],s[N],w[N],f[N];int to[N],net[N],h[N],cnt;int minn[N<<2],lazy[N<<2];int gi(){ int a=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar(); return a;}il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;}il void pushup(int rt){minn[rt]=min(minn[rt<<1],minn[rt<<1|1]);}il void pushdown(int rt){ if(lazy[rt]){ minn[rt<<1]+=lazy[rt],minn[rt<<1|1]+=lazy[rt]; lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt]; lazy[rt]=0; }}void build(int l,int r,int rt){ lazy[rt]=0; if(l==r) {minn[rt]=f[l];return;} int m=l+r>>1; build(lson),build(rson); pushup(rt);}void update(int x,int L,int R,int l,int r,int rt){ if(L<=l&&R>=r) {minn[rt]+=x,lazy[rt]+=x;return;} pushdown(rt); int m=l+r>>1; if(L<=m) update(x,L,R,lson); if(R>m) update(x,L,R,rson); pushup(rt);}int query(int L,int R,int l,int r,int rt){ if(L<=l&&R>=r) return minn[rt]; pushdown(rt); int m=l+r>>1,res=0x3f3f3f3f; if(L<=m) res=min(res,query(L,R,lson)); if(R>m) res=min(res,query(L,R,rson)); return res;}int main(){ n=gi(),k=gi()+1; For(i,2,n) d[i]=gi(); For(i,1,n) c[i]=gi(); For(i,1,n) s[i]=gi(); For(i,1,n) w[i]=gi(); ++n,d[n]=w[n]=0x3f3f3f3f; For(i,1,n) { st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d, ed[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d; if(d[ed[i]]>d[i]+s[i]) ed[i]--; add(ed[i],i); } int tot=0; For(i,1,n) { f[i]=tot+c[i]; for(RE int p=h[i];p;p=net[p]) tot+=w[to[p]]; } ans=f[n]; For(i,2,k) { build(1,n,1); For(j,1,n) { f[j]=(j>i-1?query(i-1,j-1,1,n,1):0)+c[j]; for(RE int p=h[j];p;p=net[p]) if(st[to[p]]>1) update(w[to[p]],1,st[to[p]]-1,1,n,1); } ans=min(ans,f[n]); } cout<

 

转载于:https://www.cnblogs.com/five20/p/9721158.html

你可能感兴趣的文章
ROM的一种写法
查看>>
VIM Taglist + ctags
查看>>
supervisord
查看>>
ubuntu10.04安装x264库
查看>>
●数组及应用举例
查看>>
个人作业2——英语学习APP案例分析
查看>>
Oracle中的数据字典技术初级入门
查看>>
Java Build Practice 1:Ant
查看>>
3.RxJava详解
查看>>
【小波变换】STL版 一维离散小波变换(DWT)库,完全按matlab的wavelet toolbox 的API实现的...
查看>>
作业六:小学生四则运算之NABCD模型与产品Backlog。
查看>>
__int128的实现
查看>>
R 读取clipboard内容 (MAC)
查看>>
Problem - 1118B - Codeforces(Tanya and Candies)
查看>>
jdk1.8 api 下载
查看>>
svn 图标不显示
查看>>
getElement的几中属性介绍
查看>>
iOS 使用Quartz 2D画虚线 【转】
查看>>
平面最接近点对
查看>>
HTML列表,表格与媒体元素
查看>>