2018.11.06 bzoj1835: [ZJOI2010]base 基站选址(线段树优化dp)

时间:2021-08-04 12:47:23

传送门

二分出每个点不需要付www贡献的范围,然后可以推出转移式子:

f[i][j]=f[i−1][k]+value(k+1,j)+c[i]f[i][j]=f[i-1][k]+value(k+1,j)+c[i]f[i][j]=f[i−1][k]+value(k+1,j)+c[i],把c[i]c[i]c[i]提出来发现前面的可以用线段树优化转移。

于是每次选一个数相当于区间加,再维护一个区间查询最小值转移就行了。

代码:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
using namespace std;
const int N=2e4+5,M=105;
int n,K,d[N],s[N],sum[N],w[N],c[N],ans;
inline int read(){
    int ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
vector<int>q[N];
struct Node{int l,r,add,mn;}T[N<<2];
struct Seg{int l,r;}p[N];
inline void pushup(int p){T[p].mn=min(T[lc].mn,T[rc].mn);}
inline void pushnow(int p,int v){T[p].mn+=v,T[p].add+=v;}
inline void pushdown(int p){if(T[p].add)pushnow(lc,T[p].add),pushnow(rc,T[p].add),T[p].add=0;}
inline void build(int p,int l,int r){
    T[p].l=l,T[p].r=r,T[p].add=0;
    if(l==r){T[p].mn=sum[l];return;}
    build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int ql,int qr,int v){
    if(ql>T[p].r||qr<T[p].l)return;
    if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,v);
    pushdown(p);
    if(qr<=mid)update(lc,ql,qr,v);
    else if(ql>mid)update(rc,ql,qr,v);
    else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
    pushup(p);
}
inline int query(int p,int ql,int qr){
    if(ql>T[p].r||qr<T[p].l)return 2e9;
    if(ql<=T[p].l&&T[p].r<=qr)return T[p].mn;
    pushdown(p);
    if(qr<=mid)return query(lc,ql,qr);
    if(ql>mid)return query(rc,ql,qr);
    return min(query(lc,ql,mid),query(rc,mid+1,qr));
}
int main(){
    n=read(),K=read();
    for(int i=2;i<=n;++i)d[i]=read();
    for(int i=1;i<=n;++i)c[i]=read();
    for(int i=1;i<=n;++i)s[i]=read();
    for(int i=1;i<=n;++i)w[i]=read();
    for(int i=1,l,r,pos;i<=n;++i){
        l=1,r=i-1,pos=i;
        while(l<=r){
            int Mid=l+r>>1;
            if(d[i]-d[Mid]<=s[i])r=Mid-1,pos=Mid;
            else l=Mid+1;
        }
        p[i].l=pos,l=i+1,r=n,pos=i;
        while(l<=r){
            int Mid=l+r>>1;
            if(d[Mid]-d[i]<=s[i])l=Mid+1,pos=Mid;
            else r=Mid-1;
        }
        p[i].r=pos;
    }
    for(int i=1;i<=n;++i)q[p[i].r].push_back(i);
    for(int i=1,pre=0;i<=n+1;++i){
        sum[i]=pre+c[i];
        for(int j=q[i].size()-1;~j;--j)pre+=w[q[i][j]];
    }
    ans=sum[n+1];
    for(int i=1;i<=K;++i){
        build(1,1,n+1);
        for(int j=1;j<=n+1;++j){
            sum[j]=query(1,1,j-1)+c[j];
            for(int k=0;k<q[j].size();++k)update(1,1,p[q[j][k]].l-1,w[q[j][k]]);
        }
        ans=min(ans,sum[n+1]);
    }
    cout<<ans;
    return 0;
}