【洛谷2605】[ZJOI2010] 基站选址(线段树维护DP)
大致题意: 有\(n\)个村庄,每个村庄有\(4\)个属性:\(D_i\)表示与村庄\(1\)的距离,\(C_i\)表示建立基站的费用,\(S_i\)表示能将其覆盖的建基站范围,\(W_i\)表示没建设基站所要付出的代价。
暴力\(DP\)
首先我们来考虑一波暴力\(DP\)。
设\(f_{i,j}\)为在前\(i\)村庄共建\(j\)个基站且第\(i\)个村庄必选所需的最小代价。
为了方便起见,我们定义它不管其之后的代价。
而这样统计答案又略显麻烦。
因此我们可以考虑在最后增加一个节点(\(++n\)即可),初始化其到村庄\(1\)的距离\(D_i\)和没建设基站所要付出的代价\(W_i\)为\(INF\),且建立基站的费用\(C_i\)和能将其覆盖的建基站范围\(S_i\)为\(0\)。
这样初始化的好处在于,选择这个村庄不会受到前面某个村庄的影响,而选择这个村庄又不会对答案造成任何影响。
因此,这个新的节点的答案\(f_{n,k+1}\),就是最终答案(之所以要将\(k+1\),是因为选择这个新增的节点就相当于额外多选择了一个村庄建基站)。
而就易推得转移方程:
\]
其中\(GetW\)表示\(k\)与\(i\)之间没能被覆盖、要付出额外代价的村庄的\(W_i\)之和。
这应该比较显然,就相当于枚举一个节点\(k\)作为上个建立基站的节点,然后大力转移即可。
而\(k\)从\(j-1\)开始枚举应该也是比较显然的,因为你至少要有\(j-1\)个节点才能建\(j-1\)个基站。
然而这个式子时间复杂度差不多是\(O(N^2k)\),压根不可能过。
所以就需要优化。
考虑优化
考虑到\(i\)与\(j\)的枚举顺序其实不会对答案造成任何影响,且\(i\)的转移显然比\(j\)的转移更容易优化。
因此,我们可以将\(j\)提出到最外面一层,从而转化得到如下式子:
\]
其中\(Lastf_k\)表示的是上一轮\(DP\)后\(f_k\)的值。
则对于这个式子,显然是要优化后面求\(min\)的这个过程。
考虑一个点在什么时候不会被覆盖。
这貌似是个智障的问题,题目中已经告诉我们,对于第\(i\)个基站,当距离它\(S_i\)范围内没有基站时,它就不会被覆盖。
则我们就可以求出\(L_x\)和\(R_x\)两个变量,分别表示最左边和最右边能覆盖到它的节点编号。(可用\(lower\_bound\)直接求,可惜我不知道如何处理一些细节问题,于是手写二分)
那么,容易发现,当你到第\(R_x+1\)个位置时,如果上一个基站的位置\(<L_x\),则我们就需要将代价加上\(W_x\)。
于是乎就可以发现,每次代价需增加的是一段连续的区间。
也就是说,我们需要一个算法或数据结构,能够支持区间加法和区间求和两种操作。
显然线段树。
线段树优化
考虑建一棵线段树,对于每一次更新\(j\),初始化其为\(j-1\)时的\(f\)数组。
然后,当我们操作完一个\(i\)之后,就要用所有\(R_x=i\)的\(x\)去更新选择每个点的代价。
具体操作就是在线段树上将\([j-1,L_x-1]\)这段区间权值加\(W_x\)。
而你要找到所有\(R_x=i\)的\(x\),邻接表即可。
最后每次更新,就是在线段树中询问区间\([j-1,i-1]\)中的最小值,然后加上\(C_i\)即可得到\(f_i\)。
还有,\(j=1\)时的答案需要单独预处理。
具体实现可见代码。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20000
#define K 100
#define INF 1e9
#define Gmin(x,y) (x>(y)&&(x=(y)))
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,k,ee,d[N+5],c[N+5],s[N+5],w[N+5],L[N+5],R[N+5],f[N+5],lnk[N+5];
struct edge {int to,nxt;}e[N<<1];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
class SegmentTree//线段树
{
private:
#define STO l,hl,rt<<1
#define ORZ hl+1,r,rt<<1|1
#define PU(x) (O[x]=O[x<<1]+O[x<<1|1])
#define PD(x) (O[x].F&&(O[x<<1]+=O[x].F,O[x<<1|1]+=O[x].F,O[x].F=0))
int n,v[N+5];
struct Il
{
int V,F;I Il (CI v=0,CI f=0):V(v),F(f){}
I Il operator + (Con Il& t) Con {return Il(min(V,t.V));}
I void operator += (CI x) {V+=x,F+=x;}
}O[N<<2];
I void Build(CI l,CI r,CI rt)
{
if(!(l^r)) return (void)(O[rt]=Il(v[l]));
RI hl=l+r>>1;Build(STO),Build(ORZ),PU(rt);
}
I void upt(CI l,CI r,CI rt,CI ul,CI ur,CI v)//区间加法
{
if(ul<=l&&r<=ur) return O[rt]+=v;RI hl=l+r>>1;PD(rt);
ul<=hl&&(upt(STO,ul,ur,v),0),ur>hl&&(upt(ORZ,ul,ur,v),0),PU(rt);
}
I int qry(CI l,CI r,CI rt,CI ql,CI qr)//区间求和
{
if(ql<=l&&r<=qr) return O[rt].V;RI hl=l+r>>1,res=INF,t;PD(rt);
return ql<=hl&&(t=qry(STO,ql,qr),Gmin(res,t)),qr>hl&&(t=qry(ORZ,ql,qr),Gmin(res,t)),res;
}
public:
I void Init(CI x,int* s) {for(RI i=1;i<=x;++i) v[i]=s[i];Build(1,n=x,1);}
I void Update(CI l,CI r,CI v) {l<=r&&(upt(1,n,1,l,r,v),0);}
I int Query(CI l,CI r) {return l<=r?qry(1,n,1,l,r):0;}
#undef STO
#undef ORZ
}S;
I int GP(CI x)
{
RI STO=1,hl,ORZ=n;
W(STO<=ORZ) d[hl=STO+ORZ>>1]<x?(STO=hl+1):ORZ=hl-1;
return STO;
}
int main()
{
RI i,j,p,t,ans;for(F.read(n,k),i=2;i<=n;++i) F.read(d[i]);
for(i=1;i<=n;++i) F.read(c[i]);for(i=1;i<=n;++i) F.read(s[i]);for(i=1;i<=n;++i) F.read(w[i]);
for(++n,d[n]=w[n]=INF,i=1;i<=n;++i) L[i]=GP(d[i]-s[i]),R[i]=GP(d[i]+s[i]+1)-1,add(R[i],i);//初始化L[i]和R[i],然后建边
for(t=0,i=1;i<=n;++i) for(f[i]=t+c[i],p=lnk[i];p;p=e[p].nxt) t+=w[e[p].to];ans=f[n];//预处理j=1时的答案
for(j=2;j<=k+1;++j,Gmin(ans,f[n])) for(S.Init(n,f),i=1;i<=n;++i)//枚举状态进行转移
for(f[i]=S.Query(j-1,i-1)+c[i],p=lnk[i];p;p=e[p].nxt) S.Update(j-1,L[e[p].to]-1,w[e[p].to]);//转移,并更新每个点的代价
return printf("%d",ans),0;//输出答案
}
【洛谷2605】[ZJOI2010] 基站选址(线段树维护DP)的更多相关文章
- 洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$
正解:线段树优化$dp$ 解题报告: 传送门$QwQ$ 难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$ 先考虑暴力$dp$?就设$f_{i,j}$表示 ...
- BZOJ 1835 [ZJOI2010]基站选址 (线段树优化DP)
题目大意:略 洛谷题面传送门 BZOJ题面传送门 注意题目的描述,是村庄在一个范围内去覆盖基站,而不是基站覆盖村庄,别理解错了 定义$f[i][k]$表示只考虑前i个村庄,一共建了$k$个基站,最后一 ...
- [ZJOI2010]基站选址,线段树优化DP
G. base 基站选址 内存限制:128 MiB 时间限制:2000 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离 ...
- luogu P2605 [ZJOI2010]基站选址 线段树优化dp
LINK:基站选址 md气死我了l达成1结果一直调 显然一个点只建立一个基站 然后可以从左到右进行dp. \(f_{i,j}\)表示强制在i处建立第j个基站的最小值. 暴力枚举转移 复杂度\(n\cd ...
- luogu2605 基站选址 (线段树优化dp)
设f[i][j]表示在第i个村庄建第j个基站的花费 那么有$f[i][j]=min\{f[k][j-1]+w[k,i]\}$,其中w[k,i]表示在k,i建基站,k,i中间的不能被满足的村庄的赔偿金之 ...
- 洛谷 P4747 [CERC2017]Intrinsic Interval 线段树维护连续区间
题目描述 题目传送门 分析 考虑对于 \([l,r]\),如何求出包住它的长度最短的好区间 做法就是用一个指针从 \(r\) 向右扫,每次查询以当前指针为右端点的最短的能包住 \([l,r]\) 的好 ...
- BZOJ1835: [ZJOI2010]base 基站选址(线段树优化Dp)
Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄 ...
- 洛谷 P3373 【模板】线段树 2
洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...
- Codeforces Round #271 (Div. 2) E题 Pillars(线段树维护DP)
题目地址:http://codeforces.com/contest/474/problem/E 第一次遇到这样的用线段树来维护DP的题目.ASC中也遇到过,当时也非常自然的想到了线段树维护DP,可是 ...
- codeforces Good bye 2016 E 线段树维护dp区间合并
codeforces Good bye 2016 E 线段树维护dp区间合并 题目大意:给你一个字符串,范围为‘0’~'9',定义一个ugly的串,即串中的子串不能有2016,但是一定要有2017,问 ...
随机推荐
- python3 unittest数据驱动
我们在自动化测试的时候,有没有遇到这样的问题?例如一个登录的接口要做自动化,会有很多case(用例),密码错误,密码正确这种.在继承unittest.TestCase的类中,凡是以“test”开头的方 ...
- js模仿微信语音播放的小功能
自己写的一个模仿微信语音播放的小功能,实现的主要功能是:点击播放,点击暂停,播放切换,,, 代码如下: <!DOCTYPE html> <html lang="en&qu ...
- python3 importlib模块简单利用
importlib作用:根据字符串形式导入模块,并且找到其中的类并执行 import importlib # m = importlib.import_module("src.plugins ...
- (转)Linux curl命令详解
命令:curl在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具,可以说是一款很强大的http命令行工具.它支持文件的上传和下载,是综合传输工具,但按传统,习惯称url为下载工具. ...
- express --- session详解
之前一直做前端相关的工作,所以不太清楚session,也没有主动了解,最近在学node,对session的认识又有所加深,故总结之. 注: 关于session的一些配置问题,可以看这里. 第一部分: ...
- 牛客网Java刷题知识点之泛型概念的提出、什么是泛型、泛型在集合中的应用、泛型类、泛型方法、泛型接口、泛型限定上限、泛型限定下限、 什么时候使用上限?泛型限定通配符的体现
不多说,直接上干货! 先来看个泛型概念提出的背景的例子. GenericDemo.java package zhouls.bigdata.DataFeatureSelection; import ja ...
- Hash表的原理
哈希的概念:Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩 ...
- DEDE SQL标签可以获取文档静态链接地址
在DedeCMS的系统里面,我可以通过由使用SQL语句来配合织梦标签进行更多的个性化调用.比如:推荐会员.推荐企业等.但是我们发现文档链接的底层模板地址的是动态的,那么我们要如何来进行转换,让他链接到 ...
- node搭环境(二)之 bower gulp
前面详细记录了安装node及git,接来下要安装bower,首先创建一个空文件夹bowerandgulp. 步骤:1.安装node.js 2.node里面自带了 npm 3.通过npm 安装cnpm ...
- CKEditor4.4.5 插入高度代码及上传图片
1.首先准备所需要的插件 (1). CKEditor4.4.5 下载地址:http://ckeditor.com/download.如果不想下载直接引用CKEditor的CDN也是可以的.cdn地址 ...