题意

有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为\(D_i\)

需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为\(C_i\)

如果在距离第i个村庄不超过\(S_i\)的范围内建立了一个通讯基站,那么就成它被覆盖了

如果第i个村庄没有被覆盖,则需要向他们补偿,费用为\(W_i\)

现在的问题是,选择基站的位置,使得总费用最小。

分析

朴素做法

用\(f(i,j)\)表示前\(i\)个村庄建了\(j\)个通讯站且第\(j\)个建在\(i\)处的最小代价,容易写出转移式

\[f(i,j)=\min_{k=1}^{i-1}\{f(k,j−1)+cost(k,i)\}+C_i
\]

其中\(cost(k,i)\)表示中间的补偿费用

然而这种做法时间是\(O(N \cdot K \cdot N)\),空间是\(O(N^2)\)的,不可行。

优化

如果我们外层枚举\(j\),里面枚举\(i\)呢?

很显然\(f\)的第二维和\(cost\)的第一维都可以省略。\(f\)可以用滚动,\(cost\)可以动态更新,因为变化元素的其实是有限(均摊\(O(1)\))的。

\[f(i)=\min_{k=1}^{i-1}\{f(k)+cost(k)\}+C_i
\]

由于一个村庄\(i\)被覆盖的条件是在距离第\(i\)个村庄不超过\(S_i\)的范围内建立了一个通讯基站,因此可以发现能覆盖一个村庄的基站位置应该是一个区间。

考虑第\(i\)个村庄对于的区间\([L,R]\),如果目前考虑的最后一个基站为\(R\),要转移到\(R+1\)

  1. 如果\(R\)处不建基站,那么对于相对\(R+1\)的上一个基站为\([1,L-1]\)的情况,都无法覆盖当前村庄,因此需要对\(cost\)的\([1,L-1]\)区间加\(W_i\)
  2. 如果\(R\)处建基站,那么相当于是在相对\(R+1\)的上一个基站为\([1,R-1]\)中选择一个代价最小的,相当于对之前\(f\)和\(cost\)的和区间查询最小值

用程序的语言来讲,我们用\(st_i\)和\(ed_i\)分别表示\(i\)最左端、最右端可以覆盖到\(i\)的通讯站位置,那么我们会发现当\(ed_x=i\)时,转移到\(i+1\)时\(x\)便覆盖不到了。

我们用线段树维护\(\min\{f(k)+cost(k)\}\),从\(f(i)\)变到\(f(i+1)\)时对于\(ed_x=i\)的\(x\),线段树中\([1,st_x−1]\)都加上\(W_x\)(加上补偿费用)即可

这样\(f\)就不用滚动了,\(cost\)也不用单独提出来。

外层循环枚举建站个数时每次重建线段树,复杂度\(O(K \cdot N \log N)\)

代码

  1. #include<cstdlib>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<ctime>
  6. #include<iostream>
  7. #include<string>
  8. #include<vector>
  9. #include<list>
  10. #include<deque>
  11. #include<stack>
  12. #include<queue>
  13. #include<map>
  14. #include<set>
  15. #include<bitset>
  16. #include<algorithm>
  17. #include<complex>
  18. #pragma GCC optimize ("O0")
  19. using namespace std;
  20. template<class T> inline T read(T&x)
  21. {
  22. T data=0;
  23. int w=1;
  24. char ch=getchar();
  25. while(!isdigit(ch))
  26. {
  27. if(ch=='-')
  28. w=-1;
  29. ch=getchar();
  30. }
  31. while(isdigit(ch))
  32. data=10*data+ch-'0',ch=getchar();
  33. return x=data*w;
  34. }
  35. typedef long long ll;
  36. const int INF=0x3f3f3f3f;
  37. const int MAXN=2e4+7;
  38. #define lson (o<<1)
  39. #define rson (o<<1|1)
  40. int d[MAXN],c[MAXN],s[MAXN],w[MAXN];
  41. int st[MAXN],ed[MAXN];
  42. vector<int> g[MAXN];
  43. int f[MAXN],ans;
  44. struct SegTree
  45. {
  46. int minv[MAXN<<2],addv[MAXN<<2]; // edit 3
  47. void pushup(int o)
  48. {
  49. minv[o]=min(minv[lson],minv[rson]);
  50. }
  51. void build(int o,int l,int r)
  52. {
  53. addv[o]=0; // edit 2
  54. if(l==r)
  55. {
  56. minv[o]=f[l];
  57. return;
  58. }
  59. int mid=(l+r)>>1;
  60. build(lson,l,mid);
  61. build(rson,mid+1,r);
  62. pushup(o);
  63. }
  64. void pushdown(int o)
  65. {
  66. if(addv[o])
  67. {
  68. minv[lson]+=addv[o],addv[lson]+=addv[o];
  69. minv[rson]+=addv[o],addv[rson]+=addv[o];
  70. addv[o]=0;
  71. }
  72. }
  73. int qmin(int o,int l,int r,int ql,int qr)
  74. {
  75. if(ql>qr)
  76. return 0;
  77. // cerr<<"query "<<o<<" "<<l<<" "<<r<<" "<<ql<<" "<<qr<<endl;
  78. if(ql<=l&&r<=qr)
  79. return minv[o];
  80. pushdown(o);
  81. int mid=(l+r)>>1,ans=INF;
  82. if(ql<=mid)
  83. ans=min(ans,qmin(lson,l,mid,ql,qr));
  84. if(qr>=mid+1)
  85. ans=min(ans,qmin(rson,mid+1,r,ql,qr));
  86. return ans;
  87. }
  88. void add(int o,int l,int r,int ql,int qr,int v)
  89. {
  90. if(ql>qr)
  91. return;
  92. // cerr<<"add "<<o<<" "<<l<<" "<<r<<" "<<ql<<" "<<qr<<" "<<v<<endl;
  93. if(ql<=l&&r<=qr)
  94. {
  95. minv[o]+=v,addv[o]+=v;
  96. return;
  97. }
  98. pushdown(o);
  99. int mid=(l+r)>>1;
  100. if(ql<=mid)
  101. add(lson,l,mid,ql,qr,v);
  102. if(qr>=mid+1)
  103. add(rson,mid+1,r,ql,qr,v);
  104. pushup(o);
  105. }
  106. }T;
  107. int main()
  108. {
  109. // freopen(".in","r",stdin);
  110. // freopen(".out","w",stdout);
  111. int n,m;
  112. read(n);read(m);
  113. for(int i=2;i<=n;++i)
  114. read(d[i]);
  115. for(int i=1;i<=n;++i)
  116. read(c[i]);
  117. for(int i=1;i<=n;++i)
  118. read(s[i]);
  119. for(int i=1;i<=n;++i)
  120. read(w[i]);
  121. d[++n]=INF,w[n]=INF,++m;
  122. for(int i=1;i<=n;++i)
  123. {
  124. st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
  125. ed[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
  126. if(d[ed[i]]>d[i]+s[i])
  127. --ed[i];
  128. g[ed[i]].push_back(i);
  129. }
  130. int t=0;
  131. for(int i=1;i<=n;++i)
  132. {
  133. f[i]=t+c[i];
  134. for(int j=0;j<g[i].size();++j)
  135. {
  136. int x=g[i][j];
  137. t+=w[x];
  138. }
  139. }
  140. ans=f[n];
  141. for(int i=2;i<=m;++i)
  142. {
  143. T.build(1,1,n); // edit 1
  144. for(int j=1;j<=n;++j)
  145. {
  146. f[j]=T.qmin(1,1,n,1,j-1)+c[j];
  147. for(int k=0;k<g[j].size();++k)
  148. {
  149. int x=g[j][k];
  150. T.add(1,1,n,1,st[x]-1,w[x]);
  151. }
  152. }
  153. ans=min(ans,f[n]);
  154. }
  155. printf("%d\n",ans);
  156. // fclose(stdin);
  157. // fclose(stdout);
  158. return 0;
  159. }

BZOJ1835,LG2605 [ZJOI2010]基站选址的更多相关文章

  1. Bzoj1835:[ZJOI2010]基站选址

    Sol 设\(f[i][j]\)表示钦定\(i\)建基站,建了\(j\)个基站的最小代价 \(f[i][j]=max(f[l][j-1]+\Sigma_{t=l+1}^{i-1}\)不能影响到的村庄的 ...

  2. 【LG2605】[ZJOI2010]基站选址

    [LG2605][ZJOI2010]基站选址 题面 洛谷 题解 先考虑一下暴力怎么写,设\(f_{i,j}\)表示当前\(dp\)到\(i\),且强制选\(i\),目前共放置\(j\)个的方案数. 那 ...

  3. 【题解】Luogu P2605 [ZJOI2010]基站选址

    原题传送门:P2604 [ZJOI2010]基站选址 看一眼题目,变知道这题一定是dp 设f[i][j]表示在第i个村庄修建第j个基站且不考虑i+1~n个村庄的最小费用 可以得出f[i][j] = M ...

  4. 题解 [ZJOI2010]基站选址

    题解 [ZJOI2010]基站选址 题面 解析 首先考虑一个暴力的DP, 设\(f[i][k]\)表示第\(k\)个基站设在第\(i\)个村庄,且不考虑后面的村庄的最小费用. 那么有\(f[i][k] ...

  5. luogu P2605 [ZJOI2010]基站选址 线段树优化dp

    LINK:基站选址 md气死我了l达成1结果一直调 显然一个点只建立一个基站 然后可以从左到右进行dp. \(f_{i,j}\)表示强制在i处建立第j个基站的最小值. 暴力枚举转移 复杂度\(n\cd ...

  6. [ZJOI2010]基站选址,线段树优化DP

    G. base 基站选址 内存限制:128 MiB 时间限制:2000 ms 标准输入输出 题目类型:传统 评测方式:文本比较   题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离 ...

  7. BZOJ1835 [ZJOI2010] 基站选址 【动态规划】【线段树】

    题目分析: 首先想一个DP方程,令f[m][n]表示当前在前n个村庄选了m个基站,且第m个基站放在n处的最小值,转移可以枚举上一个放基站的村庄,然后计算两个村庄之间的代价. 仔细思考两个基站之间村庄的 ...

  8. bzoj1835[ZJOI2010]基站选址

    主席树+决策单调,重写一遍比之前短多了……题解:http://www.cnblogs.com/liu-runda/p/6051422.html #include<cstdio> #incl ...

  9. [ZJOI2010]基站选址

    题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范 ...

随机推荐

  1. C#退出模式

    1.this.Close();   只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit();  强制所有消息中 ...

  2. [Android教程] Cordova开发App入门(二)使用热更新插件

    前言 不知各位遇没遇到过,刚刚发布的应用,突然发现了一个隐藏极深的“碧油鸡(BUG)”,肿么办!肿么办!肿么办!如果被老板发现,一定会让程序员哥哥去“吃鸡”.但是想要修复这个“碧油鸡”,就必须要重新打 ...

  3. 生物信息Python-从入门到精通?

    Python开发的方向太多了,有机器学习,数据挖掘,网络开发,爬虫等等.其实在生信领域,Python还显现不出绝对的优势,生信的大部分软件流程都是用shell或Perl写的,而且已经足够好用了.我选P ...

  4. TCP三次握手(待细研究)

    xu言: 看到一张不错清晰的Tcp三次握手图,收藏 Initiator  发起人 Receiver  接收者 LISTENING 状态xx服务启动后首先处于侦听(LISTENING)状态. ESTAB ...

  5. python-day20--collections模块

    1.namedtuple: 生成可以使用名字来访问元素内容的tuple >>> from collections import namedtuple >>> Poi ...

  6. hdu-1892-二维BIT

    See you~ Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Su ...

  7. UVA-11149 Power of Matrix(矩阵二分幂)

    题目大意:给一个n阶方阵,求A1+A2+A3+......Ak. 题目分析:令F(k)=A1+A2+A3+......Ak.当k为偶数时,F(k)=F(k/2)*(E+Ak/2),k为奇数时,F(k) ...

  8. JavaScript语言简介

    Web程序不论是B/S还是C/S构架,分为客户端程序与服务器端程序两种. ASP.NET是开发服务器端程序的强大工具,但有时为了降低服务器负担与通信流量,这就需要编写能够在客户端执行的程序. 脚本语言 ...

  9. swiper中的默认值的属性和作用(小程序交流群:604788754)

    swiper中的重要属性: vertical:属性,控制swiper效果是水平切换滚动,还是垂直切换滚动.如果不设置此属性,默认是水平滚动,如果设置:vertical="true" ...

  10. UVALive 5107 dfs暴力搜索

    题目链接:A hard Aoshu Problem DES:给三个字符串,包含的字符是A-E范围内的.长度都不超过8.每个字符可以而且只可以匹配一个数字.两个字符不能匹配相同的数字.前两个式子之间可以 ...