Bzoj1835:[ZJOI2010]基站选址

Sol

f[i][j] f [ i ] [ j ] 表示钦定 i i 建基站,建了j个基站的最小代价
f[i][j]=max(f[l][j1]+Σi1t=l+1 f [ i ] [ j ] = m a x ( f [ l ] [ j − 1 ] + Σ t = l + 1 i − 1 不能影响到的村庄的 w[t])+c[i] w [ t ] ) + c [ i ]

二分处理出每个村庄 p p 左右能影响到它的最远的基站设为L[p],R[p]
l,i l , i 不能影响到的即 L[p]>l,R[p]<i L [ p ] > l , R [ p ] < i

枚举 j j ,预处理出j=1的情况
线段树
每次把上次的 f f 重建进入线段树,维护最小值
再枚举i每次加入 R[p] R [ p ] 小于 i i 的覆盖1,L[p]

我是做到 f[n+1] f [ n + 1 ] ,然后做 k+1 k + 1 遍直接输出 f[n+1] f [ n + 1 ]

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(1e5 + 5);

IL ll Input(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, k, d[_], s[_], w[_], c[_], l[_], r[_], first[_], nxt[_];
int mn[_ << 2], tag[_ << 2], f[_];

IL void Build(RG int x, RG int l, RG int r){
    tag[x] = 0;
    if(l == r){
        mn[x] = f[l];
        return;
    }
    RG int mid = (l + r) >> 1;
    Build(x << 1, l, mid), Build(x << 1 | 1, mid + 1, r);
    mn[x] = min(mn[x << 1], mn[x << 1 | 1]);
}

IL void Modify(RG int x, RG int l, RG int r, RG int L, RG int R, RG int v){
    if(L <= l && R >= r){
        mn[x] += v, tag[x] += v;
        return;
    }
    RG int mid = (l + r) >> 1;
    if(L <= mid) Modify(x << 1, l, mid, L, R, v);
    if(R > mid) Modify(x << 1 | 1, mid + 1, r, L, R, v);
    mn[x] = min(mn[x << 1], mn[x << 1 | 1]) + tag[x];
}

IL int Query(RG int x, RG int l, RG int r, RG int L, RG int R){
    if(R < L) return 0;
    if(L <= l && R >= r) return mn[x];
    RG int mid = (l + r) >> 1, ans = 2e9;
    if(L <= mid) ans = Query(x << 1, l, mid, L, R);
    if(R > mid) ans = min(ans, Query(x << 1 | 1, mid + 1, r, L, R));
    return ans + tag[x];
}

int main(RG int argc, RG char *argv[]){
    Fill(first, -1), n = Input(), k = Input();
    for(RG int i = 2; i <= n; ++i) d[i] = Input();
    for(RG int i = 1; i <= n; ++i) c[i] = Input();
    for(RG int i = 1; i <= n; ++i) s[i] = Input();
    for(RG int i = 1; i <= n; ++i) w[i] = Input();
    for(RG int i = 1; i <= n; ++i){
        RG int L = 1, R = i;
        while(L <= R){
            RG int mid = (L + R) >> 1;
            if(d[i] - d[mid] <= s[i]) R = mid - 1, l[i] = mid;
            else L = mid + 1;
        }
        L = i, R = n;
        while(L <= R){
            RG int mid = (L + R) >> 1;
            if(d[mid] - d[i] <= s[i]) L = mid + 1, r[i] = mid;
            else R = mid - 1;
        }
        nxt[i] = first[r[i]], first[r[i]] = i;
    }
    for(RG int i = 1, g = 0; i <= n + 1; ++i){
        f[i] = g + c[i];
        for(RG int j = first[i]; j != -1; j = nxt[j]) g += w[j];
    }
    RG int ans = f[n + 1];
    for(RG int p = 1; p <= k; ++p){
        Build(1, 1, n);
        for(RG int i = 1; i <= n + 1; ++i){
            f[i] = Query(1, 1, n, 1, i - 1) + c[i];
            for(RG int j = first[i]; j != -1; j = nxt[j])
                if(l[j] > 1) Modify(1, 1, n, 1, l[j] - 1, w[j]);
        }
        ans = min(ans, f[n + 1]);
    }
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值