Evanyou Blog 彩带

P2605 [ZJOI2010]基站选址

题目描述

有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。

输入输出格式

输入格式:

 

输入文件的第一行包含两个整数N,K,含义如上所述。

第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。

第三行包含N个整数,表示C1,C2,…CN。

第四行包含N个整数,表示S1,S2,…,SN。

第五行包含N个整数,表示W1,W2,…,WN。

 

输出格式:

 

输出文件中仅包含一个整数,表示最小的总费用。

 

输入输出样例

输入样例#1: 
3 2
1 2
2 3 2
1 1 0
10 20 30
输出样例#1: 
4

说明

40%的数据中,N<=500;

100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。

 

Solution:

  本题线段树优化dp。

  定义状态$f[i][j]$表示到了$i$村庄建立第$j$个基站的最小费用(不考虑对$i+1\rightarrow n$村庄的影响),则不难得到状态转移方程:$f[i][j]=\min (f[k][j-1]+cost[k][i])+c[i],j-1\leq k<i$,其中$cost[k][i]$表示在$k$村庄和$i$村庄建立基站中间不会被覆盖到的村庄所需补偿费用和。

  对于$cost[k][i]$,我们完全可以预处理出每个村庄建立基站可以覆盖到的边界(二分),然后对补偿费用作前缀和,就能在转移时做到$O(1)$求$cost[k][i]$了,但是这样时间复杂度还是下不去的$O(kn^2)$。

  我们还是预处理出$st[i],ed[i]$分别表示$i$村庄建基站往左最多覆盖到的村庄和往右最多覆盖到的村庄,再用链表建立覆盖关系$ed[i]\rightarrow i$。

  状态转移时,我们可以先滚掉后面一维。然后发现每次村庄右移引起的不会被覆盖的点最多只会比上个位置不会被覆盖的点多$1$,具体来说,当我们由$f[k]$转移到状态$f[i+1]$时,若$ed[p]=i$且$k\in [1,st[p])$则转移时一定会加上覆盖$p$的费用(不在范围内的$k$位置转移的值并没有改变),那么这个区间修改区间最值的转移问题,想到用线段树来优化。

  设当前需要转移的是第$j$个基站,线段树维护$j-1$状态在每个村庄建立基站的最小费用,由某一状态转移到$i$时只需要查询$[j-1,i-1]$的最小值就好了(注意越界问题,至少要有$j-1$个基站),每次转移完就扫一下以$i$点为$ed$的节点$p$,找到$st[p]$,对$[1,st[p]-1]$这段区间的状态整体加上$w[p]$。

  在实现时由于还得知道最后的答案,我们可以在末尾放置一个哨兵村庄,将其设置为必须被选且覆盖不到任何村庄,然后令村庄数$+1$、基站数$+1$,那么每个$f[n]$就是当前状态下的答案了,取每个状态的最小值就好了。

代码:

/*Code by 520 -- 9.25*/
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N=100005;
int n,k,ans,st[N],ed[N];
int d[N],c[N],s[N],w[N],f[N];
int to[N],net[N],h[N],cnt;
int minn[N<<2],lazy[N<<2];

int gi(){
    int a=0;char x=getchar();
    while(x<'0'||x>'9') x=getchar();
    while(x>='0'&&x<='9') a=(a<<3)+(a<<1)+(x^48),x=getchar();
    return a;
}

il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;}

il void pushup(int rt){minn[rt]=min(minn[rt<<1],minn[rt<<1|1]);}

il void pushdown(int rt){
    if(lazy[rt]){
        minn[rt<<1]+=lazy[rt],minn[rt<<1|1]+=lazy[rt];
        lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt];
        lazy[rt]=0;
    }
}

void build(int l,int r,int rt){
    lazy[rt]=0;
    if(l==r) {minn[rt]=f[l];return;}
    int m=l+r>>1;
    build(lson),build(rson);
    pushup(rt);
}

void update(int x,int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r) {minn[rt]+=x,lazy[rt]+=x;return;}
    pushdown(rt);
    int m=l+r>>1;
    if(L<=m) update(x,L,R,lson);
    if(R>m) update(x,L,R,rson);
    pushup(rt);
}

int query(int L,int R,int l,int r,int rt){
    if(L<=l&&R>=r) return minn[rt];
    pushdown(rt);
    int m=l+r>>1,res=0x3f3f3f3f;
    if(L<=m) res=min(res,query(L,R,lson));
    if(R>m) res=min(res,query(L,R,rson));
    return res;
}

int main(){
    n=gi(),k=gi()+1;
    For(i,2,n) d[i]=gi();
    For(i,1,n) c[i]=gi();
    For(i,1,n) s[i]=gi();
    For(i,1,n) w[i]=gi();
    ++n,d[n]=w[n]=0x3f3f3f3f;
    For(i,1,n) {
        st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d,
        ed[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
        if(d[ed[i]]>d[i]+s[i]) ed[i]--;
        add(ed[i],i);
    }
    int tot=0;
    For(i,1,n) {
        f[i]=tot+c[i];
        for(RE int p=h[i];p;p=net[p]) tot+=w[to[p]];
    }
    ans=f[n];
    For(i,2,k) {
        build(1,n,1);
        For(j,1,n) {
            f[j]=(j>i-1?query(i-1,j-1,1,n,1):0)+c[j];
            for(RE int p=h[j];p;p=net[p]) 
                if(st[to[p]]>1) update(w[to[p]],1,st[to[p]]-1,1,n,1);
        }
        ans=min(ans,f[n]);
    }
    cout<<ans;
    return 0;
}

 

posted @ 2018-09-29 09:00  five20  阅读(298)  评论(0编辑  收藏  举报
Live2D