Luogu P2605 [ZJOI2010]基站选址

Posted cjoiershiina-mashiro

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P2605 [ZJOI2010]基站选址相关的知识,希望对你有一定的参考价值。

题目
首先设(f_{i,j})表示在第(i)个村庄修了(j)个基站的答案。
那么(f_{i,j}=c_i+minlimits_{kin[j-1,i)}(f_{k,j-1}+cost_{k,i}))
其中(cost_{k,i})表示只在(k,i)修基站的情况下((k,i))中未被覆盖的村庄的(w)之和。
我们发现(j)可以滚掉,把枚举(j)放到外面一层来,就有(f_i=c_i+minlimits_{kin[j-1,i)}(f_k+cost_{k,i}))
这东西直接算肯定没法做,我们分析一下每个部分本质上是什么。
首先我们观察(f_i)的计算式,(c_i)是完全不变的,可以无视掉。
(f_k+cost_{k,i})这个东西,在固定一个(i)时它是一个与(k)有关的变量。
那么一个比较自然的想法就是,每次将(i-1)扩展至(i)时,我们先求出(f_i)(在(i)位置修一个基站),然后对(cost)进行更新(在(i)位置不修基站)。
对于一个村庄(i),我们先预处理(st_i,ed_i),如果在([st_i,ed_i])内没有哪个村庄修了基站的话这个村庄(i)就需要被赔偿。
因为(i)位置不修基站,所以我们需要处理所有(ed_p=i)(p)村庄对(cost)的影响。
(p)村庄需要被赔偿等价于上一个基站在([1,st_p-1]),也就是从(f_1sim f_{st_p-1})转移过来的需要加上(w_p),即把(cost_{1,i}sim cost_{st_p-1,i})加上(w_p)
那么我们用线段树维护(f_k+cost_{k,i}),支持区间加和区间求最小值即可。

#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#define pb push_back
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
const int N=20007,inf=0x3f3f3f3f;
using namespace std;
int read(){int x=0,c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int max(int a,int b){return a>b? a:b;}
int min(int a,int b){return a<b? a:b;}
int d[N],c[N],s[N],w[N],f[N],st[N],ed[N],val[N<<2],tag[N<<2];
vector<int>edp[N];
void pushup(int p){val[p]=min(val[ls],val[rs]);}
void add(int p,int v){val[p]+=v,tag[p]+=v;}
void pushdown(int p){add(ls,tag[p]),add(rs,tag[p]),tag[p]=0;}
void build(int p,int l,int r)
{
    tag[p]=0;
    if(l==r) return (void)(val[p]=f[l]);
    build(ls,l,mid),build(rs,mid+1,r),pushup(p);
}
void update(int p,int l,int r,int L,int R,int v)
{
    if(L<=l&&r<=R) return (void)(tag[p]+=v,val[p]+=v);
    if(tag[p]) pushdown(p);
    if(L<=mid) update(ls,l,mid,L,R,v);
    if(R>mid) update(rs,mid+1,r,L,R,v);
    pushup(p);
}
int query(int p,int l,int r,int L,int R)
{
    if(L<=l&&r<=R) return val[p];
    if(tag[p]) pushdown(p);
    return min((L<=mid? query(ls,l,mid,L,R):inf),(R>mid? query(rs,mid+1,r,L,R):inf));
}
int main()
{
    int n=read(),k=read()+1,i,j,sum,ans;
    for(i=2;i<=n;++i) d[i]=read();
    for(i=1;i<=n;++i) c[i]=read();
    for(i=1;i<=n;++i) s[i]=read();
    for(i=1;i<=n;++i) w[i]=read();
    ++n,d[n]=w[n]=inf;
    for(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,edp[ed[i]].pb(i);
    for(i=1,sum=0;i<=n;++i)
    {
    f[i]=sum+c[i];
    for(int p:edp[i]) sum+=w[p];
    }
    ans=f[n];
    for(i=2;i<=k;++i)
    {
    build(1,1,n);
    for(j=1;j<=n;++j)
    {
        f[j]=(j>i-1? query(1,1,n,i-1,j-1):0)+c[j];
        for(int p:edp[j]) if(st[p]>1) update(1,1,n,1,st[p]-1,w[p]);
    }
    ans=min(ans,f[n]);
    }
    printf("%d",ans);
}

以上是关于Luogu P2605 [ZJOI2010]基站选址的主要内容,如果未能解决你的问题,请参考以下文章

P2605 [ZJOI2010]基站选址

P2605 [ZJOI2010]基站选址

ZJOI2010基站选址

[ZJOI2010]基站选址

[ZJOI2010]基站选址

题解:ZJOI2010基站选址 线段树+dp