神TM毒瘤线段树优化DP......新姿势get。

题意:有n个村庄,在里面选不多于k个建立基站。

建立基站要ci的费用。如果一个村庄方圆si内没有基站,那么又要支出wi的费用。求最小费用。

解:很显然想到DP,f[i][j]表示前i个村庄里面放了j个基站,其中第i个一定选的最小费用。费用只统计不超过i的。

转移就是枚举从p转移,对于p到i的每一个,检查是否需要付钱。

这样是n³k的,只有20分。

 #include <cstdio>
#include <algorithm> typedef long long LL;
const int N = ;
const LL INF = 0x3f3f3f3f3f3f3f3f; LL d[N], s[N], f[N][], c[N], w[N];
int n, first[N], last[N]; inline LL val(int l, int r) {
LL ans = ;
for(int i = l + ; i < r; i++) {
if(l < first[i] && r > last[i]) {
ans += w[i];
}
}
return ans;
} int main() {
int k;
LL ans = ;
scanf("%d%d", &n, &k);
for(int i = ; i <= n; i++) {
scanf("%lld", &d[i]);
}
for(int i = ; i <= n; i++) {
scanf("%lld", &c[i]);
}
for(int i = ; i <= n; i++) {
scanf("%lld", &s[i]);
}
for(int i = ; i <= n; i++) {
scanf("%lld", &w[i]);
ans += w[i];
f[i][] = f[i - ][] + w[i];
} for(int i = ; i <= n; i++) { // prework
int l = , r = i;
while(l < r) {
int mid = (l + r) >> ;
if(d[mid] >= d[i] - s[i]) {
r = mid;
}
else {
l = mid + ;
}
}
first[i] = r;
l = i;
r = n;
while(l < r) {
int mid = (l + r + ) >> ;
if(d[mid] <= d[i] + s[i]) {
l = mid;
}
else {
r = mid - ;
}
}
last[i] = r;
} for(int j = ; j <= k + ; j++) {
for(int i = ; i <= n + ; i++) {
// f[i][j] = std::min(f[p][j - 1] + val
if(j == ) {
f[i][j] = c[i] + val(, i);
}
else {
f[i][j] = INF;
for(int p = j - ; p < i; p++) {
f[i][j] = std::min(f[i][j], f[p][j - ] + val(p, i) + c[i]);
}
}
}
if(j > ) {
ans = std::min(ans, f[n + ][j]);
}
} printf("%lld", ans);
return ;
}

20分代码

然后我又想到了网络流,发现好像可以搞成最大权闭合子图来做,但是那个k的限制不好处理.....

最后光荣爆0了。

正解是线段树优化DP,但是怎么个优化法呢?

转移方程:f[i][j] = f[p][j - 1] + val(p, i) + c[i]

黑科技就是用线段树维护等式右边......下标就是p。

具体来说,我们当前正在考虑i。

那么f[i][j]就是min(0, i - 1),直接在线段树上查询即可。

线段树的初始值是f[p][j - 1],我们要动态的加上val(p, i)

每个点都有一个last,表示在i ~ last这段区间建基站的话能覆盖到它。

设last[v] = i,那么在i及之前的DP值都不会加上w[v],因为v之前的不会考虑到v,v ~ i的会覆盖到v。

i及之后的,有一部分转移要加上w[v],就是first[v]之前的部分。

因为first ~ i的转移会覆盖v,而i之后的转移会计算v。所以这些都不用计算v。

此时我们把0 ~ first[v] - 1的区间全部加上w[i]即可。

然后每次循环一个新j的时候把线段树初始化。

细节处理繁多......

 #include <cstdio>
#include <algorithm>
#include <vector> typedef long long LL;
const int N = ;
const LL INF = 0x3f3f3f3f3f3f3f3f; LL d[N], s[N], f[N][], c[N], w[N];
int n, first[N], last[N], Time;
std::vector<int> v[N]; LL small[N << ], tag[N << ]; inline void pushup(int o) {
small[o] = std::min(small[o << ], small[o << | ]);
return;
} inline void pushdown(int o) {
if(tag[o]) {
int ls = o << ;
int rs = ls | ;
tag[ls] += tag[o];
tag[rs] += tag[o];
small[ls] += tag[o];
small[rs] += tag[o];
tag[o] = ;
}
return;
} void add(int L, int R, LL v, int l, int r, int o) {
if(L <= l && r <= R) {
tag[o] += v;
small[o] += v;
return;
}
int mid = (l + r) >> ;
pushdown(o);
if(L <= mid) {
add(L, R, v, l, mid, o << );
}
if(mid < R) {
add(L, R, v, mid + , r, o << | );
}
pushup(o);
return;
} LL ask(int L, int R, int l, int r, int o) {
if(L <= l && r <= R) {
return small[o];
}
int mid = (l + r) >> ;
pushdown(o);
LL ans = INF;
if(L <= mid) {
ans = std::min(ans, ask(L, R, l, mid, o << ));
}
if(mid < R) {
ans = std::min(ans, ask(L, R, mid + , r, o << | ));
}
return ans;
} void clear(int l, int r, int o) {
tag[o] = ;
if(l == r) {
small[o] = f[r - ][Time - ];
return;
}
int mid = (l + r) >> ;
clear(l, mid, o << );
clear(mid + , r, o << | );
pushup(o);
return;
} int main() {
int k;
LL ans = ;
scanf("%d%d", &n, &k);
for(int i = ; i <= n; i++) {
scanf("%lld", &d[i]);
}
for(int i = ; i <= n; i++) {
scanf("%lld", &c[i]);
}
for(int i = ; i <= n; i++) {
scanf("%lld", &s[i]);
}
for(int i = ; i <= n; i++) {
scanf("%lld", &w[i]);
ans += w[i];
f[i][] = f[i - ][] + w[i];
} for(int i = ; i <= n; i++) { // prework
int l = , r = i;
while(l < r) {
int mid = (l + r) >> ;
if(d[mid] >= d[i] - s[i]) {
r = mid;
}
else {
l = mid + ;
}
}
first[i] = r;
l = i;
r = n;
while(l < r) {
int mid = (l + r + ) >> ;
if(d[mid] <= d[i] + s[i]) {
l = mid;
}
else {
r = mid - ;
}
}
last[i] = r;
v[r].push_back(i);
} for(int i = ; i <= n + ; i++) {
f[i][] = INF;
} for(int j = ; j <= k + ; j++) {
Time = j;
clear(, n + , );
for(int i = ; i <= n + ; i++) {
// ask f[i][j]
if(i >= j) {
f[i][j] = ask(, i, , n + , ) + c[i];
}
else {
f[i][j] = INF;
}
// insert
for(int p = ; p < v[i].size(); p++) {
add(, first[v[i][p]], w[v[i][p]], , n + , );
}
}
if(j > ) {
ans = std::min(ans, f[n + ][j]);
}
} printf("%lld", ans);
return ;
}

AC代码

思考:如果si表示能覆盖方圆si的村庄,又如何?

转移方程写出来,发现就是一个简单的前缀最大值优化。要处理一下前缀和为负的这种情况...

洛谷P2605 基站选址的更多相关文章

  1. [洛谷P2605] ZJOI2016 基站选址

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

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

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

  3. luogu_2605: 基站选址

    洛谷2605:基站选址 题意描述: 有\(N\)个村庄在一条直线上,第\(i(i>1)\)个村庄的距离第\(1\)个村庄的距离为\(D_i\). 需要在这些村庄中建立不超过\(K\)个通讯站,在 ...

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

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

  5. 洛谷P2514||bzoj2426 [HAOI2010]工厂选址

    洛谷P2514 bzoj2426 其实是个简单的贪心,然而不适合在脑子不清醒的时候做...看不懂题意续了1个小时 很容易发现应该枚举新建哪个发电厂,对于这种方案就是取其中b吨煤运到原来发电厂,取剩下( ...

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

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

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

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

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

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

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

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

随机推荐

  1. 20155222卢梓杰 实验五 MSF基础应用

    实验五 MSF基础应用 1.一个主动攻击实践,如ms17_010_eternalblue漏洞; 本次攻击目标是win7虚拟机 首先进行相应配置 然后点launch 就成功了 针对win7的漏洞还是相对 ...

  2. JAVA eclipse Maven项目红叹号解决方案

    我是通过 Windows --> show view --> problems 查看到发现 ch.qos.logback 1.1.1 出现了错误, 于是我换成了 ch.qos.logbac ...

  3. python语言程序设计2

    1, 代码高亮色彩体系 2, 缩进,一行代码开始前的空白区域,表达程序的格式框架 单层缩进,多层缩进 特点 概念,缩进是语法的一部分,缩进不正确的话可能会导致程序运行错误 用处(意义),是表达代码间包 ...

  4. OpenCV操作像素

    在了解了图像的基础知识和OpenCV的基础知识和操作以后,接下来我们要做的就对像素进行操作,我们知道了图像的本质就是一个矩阵,那么一个矩阵中存储了那么多的像素,我们如何来操作呢?下面通过几个例子来看看 ...

  5. LeetCode-3.无重复字符的最长字串

    给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度. 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc&qu ...

  6. 1083. List Grades (25)-简单的排序

    给定区间[L,R],给出在这区间之内的学生,并且按照他们的成绩非升序的顺序输出. #include <iostream> #include <cstdio> #include ...

  7. js实现随机的四则运算题目

    老师给出的题,写一个小程序,可以生成随机的四则运算题目给给小学生用.以前自己就写了一个四则运算的简单js小程序,是这样的: 事件 + - * / 这是个自己输入的算法,但要求是自动产生随机数.于是我用 ...

  8. Scrum Meeting day 2

              第二次会议,开发人员会议 .  •    前端: 1. 登陆界面,login, sign up, 添加加载时的图片. 2. 主界面:采用类微信类型.应含有联系人.群聊.设置 3. ...

  9. Linux内核分析(第六周)

    进程的控制与创建 一.进程的描述 1.操作系统内核的三大功能:进程管理(核心),内存管理,文件系统: 2.状态: fork() task_zombit(终止) task_running(就绪:但是没有 ...

  10. Daily Scrum - 11/25

    今天是Sprint 2的最后一天,我们在下午的课上对之前两个Sprint作了比较详尽的Review,并在课后Daily Scrum上讨论制订了Sprint 3的任务安排.具体Task会在明天更新在TF ...