[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好惡心題
題意 :有 n 個村莊,每個村莊距離1村莊 d[i] ,要求建立不多於 k個基站,在第i個村莊建基站的費用為 c[i],如果在距離村i不超過 s[i]內有基站則該村被覆蓋,村i不被覆蓋則需要賠償該村 w[i],求最少花費
然後顯然是DP
就肥腸開心的用 f[i][j] 表示到 ii 村莊建了 j個村莊的最小花費
轉移方程為 f[i][j] = max(f[i][j] , f[k][j - 1] + c[i] + cost(i , k))
然後時間復雜度就炸了
然後我們發現第二維的j沒啥卵用可以滾動掉
然而並沒有用
考慮用線段樹優化
st[i] , ed[i]表示在 st[i]~ ed[i] 間只要有一個基站點i就不需要被賠償
先單獨處理下k = 1的情況
然後我們用線段樹維護 f[]數組
之後我們第一層枚舉建幾座基站
每次枚舉都要重新按照 f[]數組重新建樹
之後我們每次查詢這個點之前的f[]數組的最小值並+c[j]
然後我們看有沒有某些城市恰可以被i覆蓋到而不能被i+1覆蓋到的,如果有則將1~st-1都加上該城市的賠償費
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 20005 ;
const int INF = 1147000000 ;
using namespace std ;
inline int read(){
char c = getchar(); int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') {x = x * 10 + c - '0' ; c = getchar() ; }
return x*w ;
}
int n , m ;
int w[M] , dis[M] , c[M] , s[M] ;
int st[M] , ed[M] ;
vector < int > p[M] ;
LL f[M] ;
LL tmin[M<<2] ;
int tag[M<<2] ;
# define ls now<<1
# define rs now<<1|1
inline void pushup(int now){
tmin[now] = min(tmin[ls] , tmin[rs]) ;
}
void Build(int l , int r , int now){
tag[now] = 0 ;
if(l == r) {
tmin[now] = f[l] ;
return ;
}
int mid = (l + r)>>1 ;
Build(l , mid , ls) ;
Build(mid + 1 , r , rs) ;
pushup(now) ;
}
inline void pushdown(int now){
if(tag[now]){
tag[ls] += tag[now] ;
tag[rs] += tag[now] ;
tmin[ls] += tag[now] ;
tmin[rs] += tag[now] ;
tag[now] = 0 ;
}
}
void Change(int L , int R , int l , int r , int x , int now){
if(L > R) return ;
if(l >= L&& r<= R){
tmin[now] += x ;
tag[now] += x ;
return ;
}
pushdown(now) ;
int mid = (l + r)>>1 ;
if(mid >= R) Change(L , R , l , mid , x , ls) ;
else if(mid < L) Change(L , R , mid + 1 , r , x , rs) ;
else{
Change(L , mid , l , mid , x , ls) ;
Change(mid + 1 , R , mid + 1 , r , x , rs) ;
}
pushup(now) ;
}
int query(int L , int R , int l , int r , int now){
if(l > R|| r < L) return INF ;
if(l >= L && r<= R) return tmin[now] ;
int mid = (l + r)>>1 ;
pushdown(now) ;
if(mid >= R) return query(L , R , l , mid , ls) ;
else if(mid < L) return query(L , R , mid + 1 , r , rs) ;
else return min(query(L , mid , l , mid , ls) , query(mid + 1 , R , mid + 1 , r , rs)) ;
}
# undef ls
# undef rs
LL Solve(){
LL Ans = 0 , temp = 0 ;
for(int i=1;i<=n;i++){
f[i] = temp + c[i] ;
for(int j=0;j<p[i].size();j++)
temp += w[p[i][j]] ;
}
Ans = f[n] ;
for(int i=2;i<=m;i++){
Build(1 , n , 1) ;
for(int j=1;j<=n;j++){
f[j] = query(1 , j - 1 , 1 , n , 1) + c[j] ;
for(int k = 0 ; k < p[j].size() ; k ++ ){
int x = p[j][k] ;
Change(1 , st[x] - 1 , 1 , n , w[x] , 1) ;
}
}
Ans = min(Ans , f[n]) ;
}
return Ans ;
}
int main(){
n = read() ; m = read() ;
for(int i=2 ; i<=n ; i++) dis[i] = read() ;
for(int i=1 ; i<=n ; i++) c[i] = read() ;
for(int i=1 ; i<=n ; i++) s[i] = read() ;
for(int i=1 ; i<=n ; i++) w[i] = read() ;
++n , ++m ; dis[n] = w[n] = INF ;
for(int i=1 ; i<=n ; i++){
int l = dis[i] - s[i] , r = dis[i] + s[i] ;
st[i] = lower_bound(dis + 1 , dis + n + 1 , l) - dis ;
ed[i] = lower_bound(dis + 1 , dis + n + 1 , r) - dis ;
while(dis[i] + s[i] < dis[ed[i]]) --ed[i] ;
p[ed[i]].push_back(i) ;
}
cout << Solve() << endl ;
return 0 ;
}
[ZJOI2010]基站選址