山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

bzoj 1835 [ZJOI2010]base 基站选址(DP+线段树)

 

【题目链接】

 

    http://www.lydsy.com/JudgeOnline/problem.php?id=1835

 

【题意】

 

    有n个村庄,每个村庄位于d[i],要求建立不多于k个基站,在第i个村庄建基站的费用为c[i],如果在距离村i不超过s[i]内有基站则该村被覆盖,村i不被覆盖的补偿费为w[i],求最少花费。

 

【思路】

 

    设f[i][j]表示第i个村建第j个基站的最小花费,则有转移式:

        f[i][j]=min{ f[k][j-1]+cost(k,i) } + c[i] ,j-1<=k<=i-1

        cost(k,i)=sigma{ w[x] } k+1<=x<=i-1 , 且x未被覆盖

    f[][]需要求一个区间最小值,我们尝试用线段树维护每一层的这个值。

    枚举j,考虑每一层i。

    我们设st[i],ed[i]分别表示在i左右距离i最远的st[i],ed[i]建基站依旧可以覆盖到i,假设我们已经求完了f[i][j]要求f[i+1][j],考虑那些恰可以被i覆盖到而不能被i+1覆盖到的,即满足ed[x]=i的点,将[1..st[x]-1]区间内的线段树值都加w[x],意为前一个基站k位于[1..st[x]-1]那么点x因不会被覆盖到需要做出赔偿。求f[i]的时候查询区间[1..i-1]内线段树值的最小即可。

    其中st[i],ed[i]可以用二分法求。

    线段树提供区间操作区间查询的操作。

    总的时间复杂度为O(nmlogn)

 

    辣鸡线段树,毁我青春(连个线段树都不会写了T^T

 

【代码】

 

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 10 using namespace std;
 11 
 12 typedef long long ll;
 13 const int N = 1e5+10;
 14 const int inf = 1e9;
 15 
 16 ll read() {
 17     char c=getchar();
 18     ll f=1,x=0;
 19     while(!isdigit(c)) {
 20         if(c=='-') f=-1; c=getchar();
 21     }
 22     while(isdigit(c))
 23         x=x*10+c-'0',c=getchar();
 24     return x*f;
 25 }
 26 
 27 int n,K; ll f[N];
 28 ll d[N],c[N],s[N],w[N],st[N],ed[N];
 29 vector<ll> ep[N];
 30 
 31 struct Tnode {
 32     int l,r; ll v,tag;
 33 }T[N<<1];
 34 
 35 void pushdown(int u)
 36 {
 37     if(T[u].l==T[u].r||(!T[u].tag)) return ;
 38     ll& t=T[u].tag;
 39     T[u<<1].v+=t,T[u<<1].tag+=t;
 40     T[u<<1|1].v+=t,T[u<<1|1].tag+=t;
 41     t=0;
 42 }
 43 void maintain(int u) 
 44 {
 45     T[u].v=min(T[u<<1].v,T[u<<1|1].v);
 46 }
 47 void build(int u,int l,int r)
 48 {
 49     T[u].l=l,T[u].r=r;
 50     T[u].tag=0;
 51     if(l==r) T[u].v=f[l];
 52     else {
 53         int mid=l+r>>1;
 54         build(u<<1,l,mid);
 55         build(u<<1|1,mid+1,r);
 56         maintain(u);
 57     }
 58 }
 59 void Add(int u,int L,int R,ll x)
 60 {
 61     if(L>R) return ;                    //处理 L>R 
 62     pushdown(u);
 63     if(L<=T[u].l&&T[u].r<=R) 
 64         T[u].v+=x,T[u].tag+=x;
 65     else {
 66         int mid=T[u].l+T[u].r>>1;
 67         if(L<=mid) Add(u<<1,L,R,x);
 68         if(mid<R) Add(u<<1|1,L,R,x);
 69         maintain(u);
 70     }
 71 }
 72 ll query(int u,int L,int R) 
 73 {
 74     if(L>R) return 0;
 75     pushdown(u);
 76     if(L<=T[u].l&&T[u].r<=R) return T[u].v;
 77     else {
 78         int mid=T[u].l+T[u].r>>1; ll ans=inf;
 79         if(L<=mid) ans=min(ans,query(u<<1,L,R));
 80         if(mid<R) ans=min(ans,query(u<<1|1,L,R));
 81         return ans;
 82     }
 83 }
 84 
 85 //lower_bound定义为找到第一个不小于v的数的指针 
 86 void init()
 87 {
 88     n=read(),K=read();
 89     FOR(i,2,n) d[i]=read();
 90     FOR(i,1,n) c[i]=read();
 91     FOR(i,1,n) s[i]=read();
 92     FOR(i,1,n) w[i]=read();
 93     n++,K++;
 94     d[n]=inf; w[n]=inf;
 95     FOR(i,1,n) {
 96         int l=d[i]-s[i],r=d[i]+s[i];
 97         l=lower_bound(d+1,d+n+1,l)-d;
 98         r=lower_bound(d+1,d+n+1,r)-d;
 99         if(d[i]+s[i]<d[r]) r--;
100         st[i]=l,ed[i]=r;
101         ep[ed[i]].push_back(i);
102     }
103 }
104 ll dp()
105 {
106     ll ans,tmp=0;
107     FOR(i,1,n) {
108         f[i]=tmp+c[i];
109         FOR(j,0,(int)ep[i].size()-1)
110             tmp+=w[ep[i][j]];
111     }
112     ans=f[n];
113     FOR(j,2,K) {
114         build(1,1,n);
115         FOR(i,1,n) {
116             f[i]=query(1,1,i-1)+c[i];
117             FOR(k,0,(int)ep[i].size()-1) {
118                 int x=ep[i][k];
119                 Add(1,1,st[x]-1,w[x]);
120             }
121         }
122         ans=min(ans,f[n]);
123     }
124     return ans;
125 }
126 
127 int main()
128 { 
129     //freopen("in.in","r",stdin);
130     //freopen("out.out","w",stdout);
131     init();
132     printf("%lld",dp());
133     return 0;
134 }

 

posted on 2016-03-17 18:42  hahalidaxin  阅读(981)  评论(0编辑  收藏  举报