[ZJOI2010]基站选址

Posted 蒟蒻ZJO :-)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ZJOI2010]基站选址相关的知识,希望对你有一定的参考价值。

题目描述

有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 2
1 2
2 3 2
1 1 0
10 20 30
输出样例#1:
4

说明

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

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

 

首先可以推出暴力的DP方程:f[i][j]表示在第i个位置建第j个基站不考虑i后面的最小费用.
转移就是f[i][j]=min(f[p][j-1]+不能覆盖的位置的补偿.)(p<i)
很明显j这一维可以滚掉.
那么现在复杂度的瓶颈在与后面的那一坨.
然后想不出来了,有一篇题解讲的很清楚:
我们假设f(j)目前已经计算完了,现在想要计算f(j+1)
我们转移每一层(1~K)时,建立一棵线段树,对于每个点k维护f(k)+cost(k,j)(注意这个东西是随着j的增长而变化的)
观察一下方程,可以发现,原方程中每个f(k),1<=k<=j-1是不随着j的增长而变化的,变化的只有cost(k,i)
是什么引起了cost(k,i)的变化呢?cost(k,i)变化到cost(k,i+1),说明有些本来能被i位置建立的一座基站覆盖到的村子,因为i->i+1而覆盖不到了,需要给他们补偿
那么我们可以预处理出每个村庄的sted,分别代表在村庄i()侧建立基站仍能覆盖到村庄i的最远村庄位置,这个可以二分求出,O(nlogn)
每次转移完f(i)时,将ed(x)=i的所有x,将[1,st[x]-1]这段区间在线段树中
加上w[x]即可.

主要思想就是动态维护后面的那一坨,找出每次会变的量和一直不变的量,然后想办法用线段树维护.


 1 #include<bits/stdc++.h>
 2 #define inf 2000000000
 3 #define maxn 20010
 4 #define ls o*2
 5 #define rs o*2+1
 6 #define mi int mid=(l+r)>>1
 7 using namespace std;
 8 int C[maxn],S[maxn],W[maxn],dis[maxn],st[maxn],ed[maxn],f[maxn],n,k;
 9 vector<int>vec[maxn];
10 int b[maxn*4],lazy[maxn*4];
11 inline void prepare(){
12   scanf("%d%d",&n,&k);
13   for(int i=2;i<=n;i++)scanf("%d",&dis[i]);
14   for(int i=1;i<=n;i++)scanf("%d",&C[i]);
15   for(int i=1;i<=n;i++)scanf("%d",&S[i]);
16   for(int i=1;i<=n;i++)scanf("%d",&W[i]);
17   dis[++n]=inf/2;W[n]=inf/2;++k;
18   for(int i=1;i<=n;i++){
19     st[i]=lower_bound(dis+1,dis+n+1,dis[i]-S[i])-dis;
20     ed[i]=upper_bound(dis+1,dis+n+1,dis[i]+S[i])-dis -1;
21     vec[ed[i]].push_back(i);
22   }
23 }
24 inline void down(int o){
25   lazy[ls]+=lazy[o],lazy[rs]+=lazy[o];
26   b[ls]+=lazy[o],b[rs]+=lazy[o];
27   lazy[o]=0;
28 }
29 void build(int o,int l,int r){
30   lazy[o]=0;
31   if(l==r){b[o]=f[l];return;}
32   mi;
33   build(ls,l,mid),build(rs,mid+1,r);
34   b[o]=min(b[ls],b[rs]);
35 }
36 int query(int o,int l,int r,int u,int v){
37   if(l!=r)down(o);
38   if(v<u) return 0;
39   if(l>v || r<u) return inf;
40   if(l>=u && r<=v) return b[o];
41   mi;
42   if(v<=mid) return query(ls,l,mid,u,v);
43   else if(u>mid) return query(rs,mid+1,r,u,v);
44   else return min(query(ls,l,mid,u,mid),query(rs,mid+1,r,mid+1,v));
45 }
46 void add(int o,int l,int r,int u,int v,int val){
47   if(l!=r)down(o);
48   if(v<u) return;
49   if(l>v || r<u) return;
50   if(l>=u && r<=v){b[o]+=val;lazy[o]+=val;return;}
51   mi;
52   if(v<=mid) add(ls,l,mid,u,v,val);
53   else if(u>mid) add(rs,mid+1,r,u,v,val);
54   else add(ls,l,mid,u,mid,val),add(rs,mid+1,r,mid+1,v,val);
55   b[o]=min(b[ls],b[rs]);
56 }
57 int main(){
58   prepare();
59   int ans=inf,tmp=0;
60   for(int i=1;i<=n;i++){
61     f[i]=tmp+C[i];
62     for(int p=0;p<vec[i].size();p++)
63       tmp+=W[vec[i][p]];
64   }
65   ans=f[n];
66   for(int j=2;j<=k;j++){
67     
68     build(1,1,n);
69     for(int i=1;i<=n;i++){
70       f[i]=query(1,1,n,1,i-1)+C[i];
71       for(int p=0;p<vec[i].size();p++){
72     int x=vec[i][p];
73     add(1,1,n,1,st[x]-1,W[x]);
74       }
75     }
76     ans=min(ans,f[n]);
77   }
78   printf("%d",ans);
79   return 0;
80 }

 

以上是关于[ZJOI2010]基站选址的主要内容,如果未能解决你的问题,请参考以下文章

P2605 [ZJOI2010]基站选址

[ZJOI2010]基站选址

[ZJOI2010]基站选址

[ZJOI 2010]base 基站选址

题解Luogu P2605 [ZJOI2010]基站选址

BZOJ1835[ZJOI2010]base 基站选址 线段树+DP