P2605 [ZJOI2010]基站选址

有 N 个村庄坐落在一条直线上,第 i(i>1) 个村庄距离第 1 个村庄的距离为 D^{i},需要在这些村庄中建立不超过 K 个通讯基站,在第 i 个村庄建立基站的费用为 C^{i}。如果在距离第 i 个村庄不超过 S^{i} 的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。

如果第 i 个村庄没有被覆盖,则需要向他们补偿,费用为 W_{i}i​。现在的问题是,选择基站的位置,使得总费用最小。

#include <cstdio>
 #include <algorithm>
 #include <vector>
 
 typedef long long LL;
 const int N = 20010;
 const LL INF = 0x3f3f3f3f3f3f3f3f;
 
 LL d[N], s[N], f[N][210], c[N], w[N];
 int n, first[N], last[N], Time;
 std::vector<int> v[N];
 
 LL small[N << 2], tag[N << 2];
 
 inline void pushup(int o) {
     small[o] = std::min(small[o << 1], small[o << 1 | 1]);
     return;
 }
 
 inline void pushdown(int o) {
     if(tag[o]) {
         int ls = o << 1;
         int rs = ls | 1;
         tag[ls] += tag[o];
         tag[rs] += tag[o];
         small[ls] += tag[o];
         small[rs] += tag[o];
         tag[o] = 0;
     }
     return;
 }
 
 void add(int L, int R, LL v, int l, int r, int o) {
     if(L <= l && r <= R) {
         tag[o] += v;
         small[o] += v;
         return;
     }
     int mid = (l + r) >> 1;
     pushdown(o);
     if(L <= mid) {
         add(L, R,  v, l, mid, o << 1);
     }
     if(mid < R) {
         add(L, R, v, mid + 1, r, o << 1 | 1);
     }
     pushup(o);
     return;
 }
 
 LL ask(int L, int R, int l, int r, int o) {
     if(L <= l && r <= R) {
         return small[o];
     }
     int mid = (l + r) >> 1;
     pushdown(o);
     LL ans = INF;
     if(L <= mid) {
         ans = std::min(ans, ask(L, R, l, mid, o << 1));
     }
     if(mid < R) {
         ans = std::min(ans, ask(L, R, mid + 1, r, o << 1 | 1));
     }
     return ans;
 }
 
 void clear(int l, int r, int o) {
     tag[o] = 0;
     if(l == r) {
         small[o] = f[r - 1][Time - 1];
         return;
     }
     int mid = (l + r) >> 1;
     clear(l, mid, o << 1);
     clear(mid + 1, r, o << 1 | 1);
     pushup(o);
     return;
 }
 
 int main() {
     int k;
     LL ans = 0;
     scanf("%d%d", &n, &k);
     for(int i = 2; i <= n; i++) {
         scanf("%lld", &d[i]);
     }
     for(int i = 1; i <= n; i++) {
         scanf("%lld", &c[i]);
     }
     for(int i = 1; i <= n; i++) {
         scanf("%lld", &s[i]);
     }
     for(int i = 1; i <= n; i++) {
         scanf("%lld", &w[i]);
         ans += w[i];
         f[i][0] = f[i - 1][0] + w[i];
     }
 
     for(int i = 1; i <= n; i++) { // prework
         int l = 1, r = i;
         while(l < r) {
             int mid = (l + r) >> 1;
             if(d[mid] >= d[i] - s[i]) {
                 r = mid;
             }
             else {
                 l = mid + 1;
             }
         }
         first[i] = r;
         l = i;
         r = n;
         while(l < r) {
             int mid = (l + r + 1) >> 1;
             if(d[mid] <= d[i] + s[i]) {
                 l = mid;
             }
             else {
                 r = mid - 1;
             }
         }
         last[i] = r;
         v[r].push_back(i);
     }
 
     for(int i = 1; i <= n + 1; i++) {
         f[i][0] = INF;
     }
 
     for(int j = 1; j <= k + 1; j++) {
         Time = j;
         clear(1, n + 1, 1);
         for(int i = 0; i <= n + 1; i++) {
             // ask f[i][j]
             if(i >= j) {
                 f[i][j] = ask(1, i, 1, n + 1, 1) + c[i];
             }
             else {
                 f[i][j] = INF;
             }
             // insert
             for(int p = 0; p < v[i].size(); p++) {
                 add(1, first[v[i][p]], w[v[i][p]], 1, n + 1, 1);
             }
         }
         if(j > 1) {
             ans = std::min(ans, f[n + 1][j]);
         }
     }
 
     printf("%lld", ans);
     return 0;
 }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值