bzoj1835 [ZJOI2010]base 基站选址 (线段树优化DP)
1835: [ZJOI2010]base 基站选址
Time Limit: 100 Sec Memory Limit: 64 MB[Submit][Status][Discuss]
Description
有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。
Input
输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。
Output
输出文件中仅包含一个整数,表示最小的总费用。
用$dp[i][j]$表示第i个基站建在第j个村庄,转移方程为$dp[i][j]=min(dp[i-1][k]+cost[k][j])+C[j]$;
$cost[i][j]$表示在从$i$到$j$所需补偿;
可以预处理出左右两侧能覆盖村庄$i$的最远基站位置$L[i]$和$R[i]$;
当$j$变为$j+1$时,可能导致一部分原本能被覆盖到的村庄不再被覆盖;
因此从$j$移动到$j+1$时,对于所有$R[k]==j$的$k$,把区间$[1,L[k]-1]$加上$W[k]$;
区间最小值+区间加,可以用线段树维护;
枚举建造基站的个数,每次重建线段树,最终$ans=min(dp[i][j]+sum[j])$;
其中$sum[j]$表示最后一个基站建在$j$时,$j$右侧村庄所需补偿,计算方式与$cost$数组类似;
复杂度$O(knlogn)$;
AC GET☆DAZE
↓代码
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<string> 6 #include<vector> 7 #include<bitset> 8 #include<cmath> 9 #include<queue> 10 #include<ctime> 11 #include<map> 12 #include<set> 13 #define N 20039 14 #define ll long long 15 #define inf 0x3f3f3f3f 16 using namespace std; 17 struct seg 18 { 19 int l,r,w,lazy; 20 }tree[N<<3]; 21 int x[N],L[N],R[N],co[N],rw[N],sum[N],dp[N],ans,stp; 22 bool use[N]; 23 vector<int> inl[N],inr[N]; 24 void pull_up(int k) 25 { 26 tree[k].w=min(tree[k<<1].w,tree[k<<1|1].w); 27 } 28 void push_down(int k) 29 { 30 if(tree[k].lazy) 31 { 32 tree[k<<1].w+=tree[k].lazy,tree[k<<1|1].w+=tree[k].lazy; 33 tree[k<<1].lazy+=tree[k].lazy,tree[k<<1|1].lazy+=tree[k].lazy; 34 tree[k].lazy=0; 35 } 36 } 37 void build(int k,int l,int r) 38 { 39 tree[k].l=l,tree[k].r=r,tree[k].lazy=0; 40 if(l==r) 41 { 42 tree[k].w=dp[l]; 43 return; 44 } 45 int mid=l+r>>1; 46 build(k<<1,l,mid); 47 build(k<<1|1,mid+1,r); 48 pull_up(k); 49 } 50 void update(int k,int l,int r,int v) 51 { 52 if(l>r) 53 { 54 return; 55 } 56 push_down(k); 57 if(tree[k].l>=l && tree[k].r<=r) 58 { 59 tree[k].w+=v; 60 tree[k].lazy+=v; 61 return; 62 } 63 int mid=tree[k].l+tree[k].r>>1; 64 if(mid>=r) 65 { 66 update(k<<1,l,r,v); 67 } 68 else if(mid<l) 69 { 70 update(k<<1|1,l,r,v); 71 } 72 else 73 { 74 update(k<<1,l,r,v),update(k<<1|1,l,r,v); 75 } 76 pull_up(k); 77 } 78 int query(int k,int l,int r) 79 { 80 if(l>r) 81 { 82 return 0; 83 } 84 push_down(k); 85 if(tree[k].l>=l && tree[k].r<=r) 86 { 87 return tree[k].w; 88 } 89 int mid=tree[k].l+tree[k].r>>1; 90 if(mid>=r) 91 { 92 return query(k<<1,l,r); 93 } 94 else if(mid<l) 95 { 96 return query(k<<1|1,l,r); 97 } 98 else 99 { 100 return min(query(k<<1,l,r),query(k<<1|1,l,r)); 101 } 102 } 103 int main() 104 { 105 int n,k,a,b,c; 106 scanf("%d%d",&n,&k); 107 for(a=2;a<=n;a++) 108 { 109 scanf("%d",&x[a]); 110 } 111 for(a=1;a<=n;a++) 112 { 113 scanf("%d",&co[a]); 114 } 115 for(a=1;a<=n;a++) 116 { 117 scanf("%d",&b); 118 L[a]=lower_bound(x+1,x+n+1,x[a]-b)-x; 119 R[a]=lower_bound(x+1,x+n+1,x[a]+b)-x; 120 if(x[R[a]]>x[a]+b) 121 { 122 R[a]--; 123 } 124 inl[L[a]].push_back(a); 125 inr[R[a]].push_back(a); 126 } 127 for(a=1;a<=n;a++) 128 { 129 scanf("%d",&rw[a]); 130 ans+=rw[a]; 131 } 132 for(a=1,stp=ans;a<=n;a++) 133 { 134 for(b=0;b<inl[a].size();b++) 135 { 136 if(!use[inl[a][b]]) 137 { 138 stp-=rw[inl[a][b]]; 139 } 140 use[inl[a][b]]=1; 141 } 142 sum[a]=stp; 143 } 144 for(a=1;a<=k;a++) 145 { 146 if(a==1) 147 { 148 for(b=1,stp=0;b<=n;b++) 149 { 150 dp[b]=stp+co[b]; 151 for(c=0;c<inr[b].size();c++) 152 { 153 stp+=rw[inr[b][c]]; 154 } 155 ans=min(ans,dp[b]+sum[b]); 156 } 157 continue; 158 } 159 build(1,1,n); 160 for(b=1;b<=n;b++) 161 { 162 dp[b]=query(1,1,b-1)+co[b]; 163 for(c=0;c<inr[b].size();c++) 164 { 165 update(1,1,L[inr[b][c]]-1,rw[inr[b][c]]); 166 } 167 ans=min(ans,dp[b]+sum[b]); 168 } 169 } 170 printf("%d",ans); 171 return 0; 172 }
散りぬべき 時知りてこそ 世の中の 花も花なれ 人も人なれ