zoukankan      html  css  js  c++  java
  • [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);
    }
    
  • 相关阅读:
    UNIX环境高级编程 第9章 进程关系
    UNIX环境高级编程 第8章 进程控制
    UNIX环境高级编程 第7章 进程环境
    最小截断[AHOI2009]
    切糕[HNOI2013]
    餐巾
    happiness[国家集训队2011(吴确)]
    奇怪的道路[JXOI2012]
    王者之剑
    抵制克苏恩[Lydsy2017年4月月赛]
  • 原文地址:https://www.cnblogs.com/dsjkafdsaf/p/11258484.html
Copyright © 2011-2022 走看看