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 阅读(1000) 评论(0) 编辑 收藏 举报