[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而覆盖不到了,需要给他们补偿
那么我们可以预处理出每个村庄的st和ed,分别代表在村庄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 }