[ZJOI2010]基站选址

[ZJOI2010]基站选址

BZOJ权限
luogu
考虑朴素的dp,\(f[i][j]=min(f[k][j]+cost(k,i))\)
cost(k,i)即在k,i建站中间一些没有被覆盖的点的代价和
二分出在每个点建站可以覆盖到的区间\(st_i,ed_i\)
那么cost就很好计算了
但这个复杂度依然不对
我们考虑线段树维护这个东西
把j放到最外面枚举
那么转移只要查询维护1~i-1的f[k]+cost(k,i)的最小值就行
然而cost(k,i)这东西是动态的,假设\(ed_p==i\)
我们每次转移i+1时把[1.p]的代价加上\(w_p\)就好
每次做完一层重新build
还有一个细节,为了统计到\(ed_p==n\)的这些贡献,你需要在最后新加一个dinf,winf的点
复杂度\(O(nklogn)\)

#define ls x<<1,l,mid
#define rs x<<1|1,mid+1,r
#include<bits/stdc++.h>
using namespace std;
const int _=2e4+5;
int re(){
    int x=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}
int n,m,sum,ans;
vector<int>v[_];
int d[_],c[_],s[_],w[_],f[_],st[_],ed[_],mn[_<<2],t[_<<2];
void pu(int x){mn[x]=min(mn[x<<1],mn[x<<1|1]);}
void bu(int x,int l,int r){
    t[x]=0;if(l==r){mn[x]=f[l];return;}
    int mid=l+r>>1;bu(ls);bu(rs);pu(x);
}
void cov(int x,int v){mn[x]+=v;t[x]+=v;}
void pd(int x){cov(x<<1,t[x]);cov(x<<1|1,t[x]);t[x]=0;}
void add(int x,int l,int r,int ql,int qr,int v){
    if(ql<=l&&r<=qr){cov(x,v);return;}if(t[x])pd(x);
    int mid=l+r>>1;if(ql<=mid)add(ls,ql,qr,v);
    if(qr>mid)add(rs,ql,qr,v);pu(x);
}
int qmin(int x,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr)return mn[x];if(t[x])pd(x);
    int mid=l+r>>1,res=1e9;if(ql<=mid)res=qmin(ls,ql,qr);
    if(qr>mid)res=min(res,qmin(rs,ql,qr));return res;
}
int main(){
    n=re()+1,m=re();
    for(int i=2;i<n;i++)d[i]=re();d[n]=1e9;
    for(int i=1;i<n;i++)c[i]=re();
    for(int i=1;i<n;i++)s[i]=re();
    for(int i=1;i<n;i++)w[i]=re();w[n]=1e9;
    for(int i=1;i<=n;i++){
        st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
        ed[i]=upper_bound(d+1,d+n+1,d[i]+s[i])-d-1;
        v[ed[i]].push_back(i);
    }
    for(int i=1;i<=n;i++){
        f[i]=sum+c[i];
        for(int j=0,k=v[i].size();j<k;j++)
            sum+=w[v[i][j]];
    }
    ans=f[n];
    while(m--){
        bu(1,1,n);
        for(int i=1;i<=n;i++){
            if(i>1)f[i]=qmin(1,1,n,1,i-1)+c[i];
            else f[i]=c[i];
            for(int j=0,k=v[i].size();j<k;j++){
                int x=v[i][j];
                if(st[x]>1)add(1,1,n,1,st[x]-1,w[x]);
            }
        }
        ans=min(ans,f[n]);
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-11-02 14:32  sdzwyq  阅读(153)  评论(0编辑  收藏  举报