P2605 [ZJOI2010]基站选址

题目

P2605 [ZJOI2010]基站选址

简化题目:1~n个村庄,每个村庄有范围,要求范围内有村庄被选(不超过(k)个),否则被罚款,选村庄有一定花费,求最小总花费

做法

朴素
(dp_{i,j})(j)个被选的村庄为(i)的最小花费:(dp_{i,k}=min{dp_{j,k-1}+cost_{i,j}}+w_i)

枚举(k)(准备选第(k)个),(i)(具体选(i)),(j)(上一个被选的),显然(cost_{i,j})预处理(O(n^2)),枚举方程(O(n^2k))

时间复杂度度估为(O(n^2k))

优化
习惯地,只出现(k)(k-1),倒序枚举(j)(dp_{i}=min{dp_{j}+cost_{i,j}}+w_i),还只是优化了点空间而已

最外层(k)其实差不多可以不管了((k<=100)),主要是优化刚才的这个方程

无后效性,顺序也显然((j<i)),考虑这部分(min{dp_{j}+cost_{i,j}}),最值有关的((BIT)/线段树,单调队列,斜率优化......)

有哪些相同的部分可以利用呢?

假设这次是选(x),上一次选(i),那(i)前面的我们暂时不管,因为之前已经计算过了,我们只用考虑(i)~(k)中间的赔款

对于上一次选的位置(i)(j),如果(i<j),上一个选(i)的赔款肯定包括了(longrightarrow)上一个选(j)的赔款

理解:(i)~(x)(j)~(x)中间的空区比较多

已经接近了我们的最终思路,假设我们这次在做第(k)个选(i),设以(i)为右端点的村庄为(x),其左端点为(L),那我们

(1)~(L-1)都加上(pay_{x})村庄(x)的赔款:选(1)~(L-1)的村庄就不符合(x)

诶!!区间查询最小值及位置,区间修改好像线段树做会比较简单

至此,我们已经得到了最终解法:最外层枚举(k)(选第k个),第二层枚举选(i)(选第i个村庄),然后线段树维护选每个点的赔款及区间最小值

My complete code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
typedef int LL;
const LL maxn=20000+9,inf=0x3f3f3f3f;
inline LL Read(){
    LL x(0),f(1); char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') f=-1;c=getchar();
    }
    while(c>='0'&&c<='9')
        x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*f;
}
struct node{
    LL mn,mni,lazy;
    LL son[2];
}T[maxn<<2];
LL n,ans,nod,K,root;
LL d[maxn],s[maxn],bad[maxn],pay[maxn],dp[maxn],st[maxn],ed[maxn];
vector<LL> belong[maxn];
inline void Update(LL now,LL s1,LL s2){
    if(T[s1].mn<T[s2].mn)
        T[now].mn=T[s1].mn,
        T[now].mni=T[s1].mni;
    else
        T[now].mn=T[s2].mn,
        T[now].mni=T[s2].mni;
}
void Build(LL &now,LL l,LL r){
    if(!now)
        now=++nod;
    T[now].mn=T[now].mni=T[now].lazy=0;
    if(l==r){
        T[now].mn=dp[l],
        T[now].mni=l;
        return;
    }
    LL mid(l+r>>1);
    Build(T[now].son[0],l,mid),
    Build(T[now].son[1],mid+1,r);
    Update(now,T[now].son[0],T[now].son[1]);
}
inline void Pushdown(LL now,LL s1,LL s2){
    LL val(T[now].lazy);
    T[now].lazy=0;
    if(!val)
        return;
    T[s1].lazy+=val,T[s2].lazy+=val,
    T[s1].mn+=val,T[s2].mn+=val;
}
LL Query(LL now,LL l,LL r,LL lt,LL rt){
    if(lt>rt)
        return inf;
    if(lt<=l&&rt>=r)
        return T[now].mn;
    Pushdown(now,T[now].son[0],T[now].son[1]);
    LL mid(l+r>>1),ret=inf;
    if(lt<=mid)
        ret=min(ret,Query(T[now].son[0],l,mid,lt,rt));
    if(rt>mid)
        ret=min(ret,Query(T[now].son[1],mid+1,r,lt,rt));
    return ret;
}
inline void Change(LL now,LL l,LL r,LL lt,LL rt,LL val){
    if(lt>rt)
        return;
    if(lt<=l&&rt>=r){
        T[now].mn+=val,
        T[now].lazy+=val;
        return;
    }
    Pushdown(now,T[now].son[0],T[now].son[1]);
    LL mid(l+r>>1);
    if(lt<=mid)
        Change(T[now].son[0],l,mid,lt,rt,val);
    if(rt>mid)
        Change(T[now].son[1],mid+1,r,lt,rt,val);
    Update(now,T[now].son[0],T[now].son[1]);
}
int main(){
    n=Read(),K=Read();
    for(LL i=2;i<=n;++i)
        d[i]=Read();
    for(LL i=1;i<=n;++i)
        pay[i]=Read();
    for(LL i=1;i<=n;++i)
        s[i]=Read();
    for(LL i=1;i<=n;++i)
        bad[i]=Read();
    d[++n]=inf;
    for(LL i=1;i<=n;++i){
        st[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d,
        ed[i]=lower_bound(d+1,d+1+n,d[i]+s[i])-d;
        if(d[ed[i]]>d[i]+s[i])
            --ed[i];
        belong[ed[i]].push_back(i);
    }
    LL sum(0);
    for(LL i=1;i<=n;++i){
        dp[i]=sum+pay[i];
        for(LL j=0,size=belong[i].size();j<size;++j)
            sum+=bad[belong[i][j]];
    }
    ans=dp[n];
    ++K;
    for(LL k=2;k<=K;++k){
        Build(root,1,n);
        for(LL i=1;i<=n;++i){
            dp[i]=Query(root,1,n,1,i-1)+pay[i];
            for(LL j=0,size=belong[i].size();j<size;++j){
                LL x(belong[i][j]);
                Change(root,1,n,1,st[x]-1,bad[x]);
            }
        }
        ans=min(ans,dp[n]);
    }
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/y2823774827y/p/10274063.html