传送门

题意

有 $ n $ 个村庄在一排直线上,现在要建造不超过 $ K $ 个通讯基站,基站只能造在村庄处。

第 $ i $ 个村庄距离第 $ 1 $ 个村庄的距离为 $ D_i $ 。在此建造基站的费用为 $ C_i $ 。如果在此不超过 $ S_i $ 的范围内有基站,那么这个村庄就被覆盖了。如果它没有被覆盖,则需要花费 $ W_i $ 的补偿费用。

问你最小总花费是多少。

题解

首先有一个很显然的dp:

$ dp[i][j] $ 表示在第 $ i $ 个村庄建了基站,此时一共建了 $ j $ 个基站,第 $ i $ 个以及之前村庄的最小花费。

转移为:

\[dp[i][j] = min(dp[k][j-1] + cost(k,i)) \quad (k<i)
\]

其中 $ cost(k,i) $ 表示如果在 $ k $ 和 $ i $ 建了基站,并且它们之间没有建基站,此时村庄 $ k $ 到村庄 $ i $ 之间的花费,即:

\[cost(i,j) = \sum_{k=i}^j W_k * [|D_k-D_i|>S_k \text{ } \& \text{ } |D_k-D_j|>S_k]
\]

然后考虑如何优化。

先定义 $ l_i $ 和 $ r_i $ 分别表示能够覆盖村庄 $ i $ 的最左和最右的基站建造位置,可以直接二分出来。

我们希望对于每个 $ j $ 来说,在 $ i $ 不断递增的过程中,动态地维护 $ F(k) = dp[k][j-1] + cost(k,i) $ 的最小值。

假设当前已经求出了 $ dp[i-1][j] $ ,接下来该求 $ dp[i][j] $ 了。

那么对于所有满足 $ r_p = i-1 $ 的 $ p $ 来说,所有 $ F(k) \quad (k \in [1,l_p-1]) $ 都应该加上 $ W_p $ 。也就是将基站从 $ i-1 $ 移动到 $ i $ 之后,给对应的 $ F(k) $ 加上了那些本来被覆盖但是现在不覆盖的村庄的补偿费。

所以对于当前的 $ i ​$ 来说,$ dp[i][j] = min(F(k)) \quad (k \in [1,i-1]) ​$

由于有区间加法和查区间最小值,所以要用线段树维护 $ F(k) $ 。对于每个 $ j $ 来说,在一开始重建一下线段树就好。

AC Code

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define MAX_N 20005
#define MAX_V 80005
#define INF 1000000000
#define int long long using namespace std; int n,K,ans;
int d[MAX_N];
int c[MAX_N];
int s[MAX_N];
int w[MAX_N];
int l[MAX_N];
int r[MAX_N];
int dp[MAX_N];
int dat[MAX_V];
int tag[MAX_V];
vector<int> v[MAX_N]; void read()
{
scanf("%lld%lld",&n,&K);
for(int i=2;i<=n;i++) scanf("%lld",&d[i]);
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
for(int i=1;i<=n;i++) scanf("%lld",&s[i]);
for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
d[++n]=INF,w[n]=INF,K++;
} void cal_lr()
{
for(int i=1;i<=n;i++)
{
l[i]=lower_bound(d+1,d+1+n,d[i]-s[i])-d;
r[i]=upper_bound(d+1,d+1+n,d[i]+s[i])-d-1;
v[r[i]].push_back(i);
}
} void push_down(int k)
{
if(tag[k])
{
dat[k*2+1]+=tag[k];
dat[k*2+2]+=tag[k];
tag[k*2+1]+=tag[k];
tag[k*2+2]+=tag[k];
tag[k]=0;
}
} void push_up(int k)
{
dat[k]=min(dat[k*2+1],dat[k*2+2]);
} void build(int l,int r,int k)
{
if(l==r)
{
dat[k]=dp[l];
tag[k]=0;
return;
}
int mid=(l+r)>>1;
build(l,mid,k*2+1);
build(mid+1,r,k*2+2);
push_up(k);
tag[k]=0;
} void update(int a,int b,int k,int l,int r,int x)
{
if(a<=l && r<=b)
{
dat[k]+=x;
tag[k]+=x;
return;
}
push_down(k);
int mid=(l+r)>>1;
if(a<=mid) update(a,b,k*2+1,l,mid,x);
if(b>mid) update(a,b,k*2+2,mid+1,r,x);
push_up(k);
} int query(int a,int b,int k,int l,int r)
{
if(a<=l && r<=b) return dat[k];
push_down(k);
int mid=(l+r)>>1,ans=INF;
if(a<=mid) ans=min(ans,query(a,b,k*2+1,l,mid));
if(b>mid) ans=min(ans,query(a,b,k*2+2,mid+1,r));
return ans;
} void cal_dp()
{
int sum=0;
for(int i=1;i<=n;i++)
{
dp[i]=sum+c[i];
for(int j=0;j<v[i].size();j++) sum+=w[v[i][j]];
}
ans=dp[n];
for(int j=2;j<=K;j++)
{
build(1,n,0);
for(int i=1;i<=n;i++)
{
if(i>1) dp[i]=query(1,i-1,0,1,n)+c[i];
else dp[i]=c[i];
for(int k=0;k<v[i].size();k++)
{
int t=v[i][k];
if(l[t]>1) update(1,l[t]-1,0,1,n,w[t]);
}
}
ans=min(ans,dp[n]);
}
} void work()
{
cal_lr();
cal_dp();
printf("%lld\n",ans);
} signed main()
{
read();
work();
}

BZOJ 1835 [ZJOI2010]base 基站选址:线段树优化dp的更多相关文章

  1. BZOJ1835: [ZJOI2010]base 基站选址(线段树优化Dp)

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

  2. BZOJ 1835: [ZJOI2010]base 基站选址 [序列DP 线段树]

    1835: [ZJOI2010]base 基站选址 题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立 ...

  3. 【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

    [BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯 ...

  4. 洛谷$P2605\ [ZJOI2010]$基站选址 线段树优化$dp$

    正解:线段树优化$dp$ 解题报告: 传送门$QwQ$ 难受阿,,,本来想做考试题的,我还造了个精妙无比的题面,然后今天讲$dp$的时候被讲到了$kk$ 先考虑暴力$dp$?就设$f_{i,j}$表示 ...

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

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

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

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1835 [题意] 有n个村庄,每个村庄位于d[i],要求建立不多于k个基站,在第i个村庄 ...

  7. BZOJ 1835: [ZJOI2010]base 基站选址(DP,线段树)

    可以很容易的写出dp方程: F[i][j]=min(F[l][j-1]+w[l][i])+c[i] (w[i][j]是从l+1到i-1这些点p里,所有满足d[p]+s[p]<d[i] & ...

  8. BZOJ 1835 [ZJOI2010]基站选址 (线段树优化DP)

    题目大意:略 洛谷题面传送门 BZOJ题面传送门 注意题目的描述,是村庄在一个范围内去覆盖基站,而不是基站覆盖村庄,别理解错了 定义$f[i][k]$表示只考虑前i个村庄,一共建了$k$个基站,最后一 ...

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

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

随机推荐

  1. Fluent Ribbon 第八步 其他控件

    前七节将Ribbon的功能大致介绍了一番,本节来介绍一些特殊控件的使用 DropDownButton控件 当前控件是显示下拉功能的基本组件,其配合Gallery能实现诸多特殊功能,代码如下所示 < ...

  2. Linux磁盘分区的理解

    磁盘分割: 一块磁盘可以被分割为多个分区artition. 磁盘链接的方式 正常的实体机使用的都是/dev/sd[a-]的硬盘档名 虚拟机可能会使用/dev/vd[a-p]这种装置档名 SATA/US ...

  3. Spoken English Practice(I won't succumb to you, not ever again)

    绿色:连读:                  红色:略读:               蓝色:浊化:               橙色:弱读     下划线_为浊化 口语蜕变(2017/6/28) ...

  4. Python菜鸟之路:Django CMDB剖析

    CMDB需求分析 1. 采集资产 采集方式的配置(agent/salt/ssh三种方式) agent是在本地执行 salt和ssh则是在远端执行,首先通过API获取任务,然后去远端获取执行结果 插件的 ...

  5. Spring 框架的 applicationContext.xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  6. Spring 的IOC容器之注解的方式

    1. 环境搭建 1.1 导入所需 jar 包 引入 IOC 容器必须的6个jar包; spring-aop-4.3.10.RELEASE.jar, Spring 框架的AOP的jar包; 1.2 创建 ...

  7. python中如何去掉unicode编码前面的u?

    1.列表类型(用join方法) myUlist = [u'AB', u'AB', u'AB', u'AB'] print myUlist print ", ".join(myUli ...

  8. 分别用request和socket给百多发送请求

    1.方式1 import socket client = socket.socket() # 百度创建连接: 阻塞 client.connect(('www.baidu.com',80)) # 问百度 ...

  9. 部门人员能力模型的思考:海军 or 海盗——By Me

    我们欢迎您的加入,与我们一起推动安全可视化团队的成长,实现技术上共同进步和感情上的更多互相支持!

  10. hexo+yilia主题博客如何添加图标icon

    1. 先去比特虫网站做icon图标 2. 图片放到hexo/source/img文件夹下 3. 找到hexo\themes\modernist\layout_partial\head.ejs,设置为 ...