[BZOJ1835][ZJOI2010]base 基站选址

题目描述

\(N\)个村庄坐落在一条直线上,第\(i(i>1)\)个村庄距离第\(1\)个村庄的距离为\(D_i\)

需要在这些村庄中建立不超过\(K\)个通讯基站,在第\(i\)个村庄建立基站的费用为\(C_i\)

如果在距离第\(i\)个村庄不超过\(S_i\)的范围内建立了一个通讯基站,那么就成它被覆盖了。

如果第\(i\)个村庄没有被覆盖,则需要向他们补偿,费用为\(W_i\)

现在的问题是,选择基站的位置,使得总费用最小。

40%的数据中,\(N<=500\);

100%的数据中,\(K<=N,K<=100,N<=20000,D_i<=1e9,C_i<=1e4,S_i<=1e9\);

Input

输入第一行包含两个整数\(N,K\),含义如上所述。

第二行包含\(N−1\)个整数,分别表示\(D_2,D_3,…,D_N\) ,这\(N−1\)个数是递增的。

第三行包含\(N\)个整数,表示\(C_1,C_2,…,C_N\)

第四行包含\(N\)个整数,表示\(S_1,S_2,…,S_N\)

第五行包含\(N\)个整数,表示\(W_1,W_2,…,W_N\)

Output

一个整数,表示最小的总费用

Sample Input

3 2
1 2
2 3 2
1 1 0
10 20 30

Sample Output

4

可以很方便的看出朴素的\(dp\);

\(dp[i][j]=dp[k][j-1]+ctk[k][i]+cost[i],0<j<i\)

其中,\(ctk[i][j]\)表示当时的情况下\(i-j\)的补偿总和。

\(O(n)\)统计\(ctk[i][j]\),总时间复杂度为\(O(n^2*k)\)

在利用刷表法,可以在空间上利用滚动数组消掉一维。

同时,利用线段树维护区间最小值降掉一个时间复杂度。

此时,总时间复杂度都堆积到统计\(ctk[i][j]\)上。

应该如何快速的统计\(ctk[i][j]\)呢?

我们先处理出,一个村庄能被覆盖的左右端点。

在一个村庄的右端点上储存该村庄的编号。

先利用前缀和统计出\(dp\)数组的初值。

考虑一个点的\(dp\)初值,在该点往右不能覆盖到的端点,自然在下一个点上也无法覆盖到,直接累加不能覆盖到的点的补偿值即可。

决策单调性

每次,只用建线段树,利用区间最值去更新即可。

代码如下

#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
 
using namespace std;
 
#define LL long long
#define reg register
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,e) for(reg int i=H[e]; i; i=G[i].nxt)
 
inline int Read() {
    int res=0,f=1;
    char c;
    while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),c>=48&&c<=57);
    return f?res:-res;
}
 
template<class T>inline bool Min(T &a,T const&b) {
    return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a,T const&b) {
    return a<b?a=b,1:0;
}
const int N=2e4+5,M=2e6+5;
 
int n,m,dis[N],cost[N],W[N],S[N],dp[N],L[N],R[N];
 
struct SetmentTree {
    int Mi[N<<2],lazy[N<<2];
     
    void up(int x) {
        Mi[x]=min(Mi[x<<1],Mi[x<<1|1]);
    }
    void down(int x) {
        if(!lazy[x])return;
        Mi[x<<1]+=lazy[x];
        Mi[x<<1|1]+=lazy[x];
        lazy[x<<1]+=lazy[x];
        lazy[x<<1|1]+=lazy[x];
        lazy[x]=0;
    }
    void build(int L,int R,int num) {
        lazy[num]=0;
        if(L==R) {
            Mi[num]=dp[L];
            return;
        }
        int mid=(L+R)>>1;
        build(L,mid,num<<1);
        build(mid+1,R,num<<1|1);
        up(num);
    }
    void update(int L,int R,int l,int r,int num,int x){
        if(l>r)return;
        if(l==L&&r==R){
            lazy[num]+=x;
            Mi[num]+=x;
            return;
        }
        down(num);
        int mid=(L+R)>>1;
        if(r<=mid)update(L,mid,l,r,num<<1,x);
        else if(l>mid)update(mid+1,R,l,r,num<<1|1,x);
        else {
            update(L,mid,l,mid,num<<1,x);
            update(mid+1,R,mid+1,r,num<<1|1,x);
        }
        up(num);
    }
    int query(int L,int R,int l,int r,int num){
        if(l>r)return 0;
        if(l==L&&r==R)return Mi[num];
        down(num);
        int mid=(L+R)>>1;
        if(r<=mid)return query(L,mid,l,r,num<<1);
        else if(l>mid)return query(mid+1,R,l,r,num<<1|1);
        else return min(query(L,mid,l,mid,num<<1),query(mid+1,R,mid+1,r,num<<1|1));
    }
} Tree;
 
vector<int>edge[N];
 
signed main() {
    n=Read(),m=Read();
    rep(i,2,n)dis[i]=Read();
    rep(i,1,n)cost[i]=Read();
    rep(i,1,n)S[i]=Read();
    rep(i,1,n)W[i]=Read();
    n++,dis[n]=S[n]=W[n]=1e9;//设立监视哨 
    rep(i,1,n){
        L[i]=lower_bound(dis+1,dis+n+1,dis[i]-S[i])-dis;//左端点 
        R[i]=upper_bound(dis+1,dis+n+1,dis[i]+S[i])-dis-1;//右端点 
        edge[R[i]].push_back(i);//存点 
    }
    int Sum=0;
    rep(i,1,n){
        dp[i]=Sum+cost[i];
        ret(j,0,edge[i].size())Sum+=W[edge[i][j]];//统计补偿和 
    }
    int Ans=dp[n];
    rep(i,1,m){
        Tree.build(1,n,1);
        rep(j,1,n){
            dp[j]=Tree.query(1,n,1,j-1,1)+cost[j];
            ret(k,0,edge[j].size()){
                int y=edge[j][k];
                Tree.update(1,n,1,L[y]-1,1,W[y]);
            }
        }
        Min(Ans,dp[n]);
    }
    printf("%d",Ans);
}
posted @ 2019-07-28 12:27  dsjkafdsaf  阅读(134)  评论(0编辑  收藏  举报