1835: [ZJOI2010]base 基站选址

题目描述

有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述。 第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。 第三行包含N个整数,表示C1,C2,…CN。 第四行包含N个整数,表示S1,S2,…,SN。 第五行包含N个整数,表示W1,W2,…,WN。

输入输出格式

输入格式:

输入文件的第一行包含两个整数N,K,含义如上所述。

第二行包含N-1个整数,分别表示D2,D3,…,DN ,这N-1个数是递增的。

第三行包含N个整数,表示C1,C2,…CN。

第四行包含N个整数,表示S1,S2,…,SN。

第五行包含N个整数,表示W1,W2,…,WN。

输出格式:

输出文件中仅包含一个整数,表示最小的总费用。

输入输出样例

输入样例#1:

3 2
1 2
2 3 2
1 1 0
10 20 30
输出样例#1:

4

说明

40%的数据中,N<=500;

100%的数据中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。


到洛谷偷了题面

理解了题解之后还是比较好写的

显然f[i][j]表示前i个村子建了j个基站且第i个村子建了基站

f[i][j]=c[i]+min{f[k][j-1]+cost(k,j)}

cost(k,j)表示k有一个基站,j有一个基站,k..j的补偿代价

关键就是快速计算这个东西了

线段树优化,就是用线段树区间min来logn获得转移来的状态中最小值吧

j这一维显然可以滚掉

想办法让线段树每个点表示了选这个点作为转移点时的代价

先把f[][j-1]建树,然后处理cost的问题

当i-->i+1时,发现左端点不变,右段点右移了,那么哪些刚好最远i位置可以覆盖到的点就可能要补偿了

所以对于点x,通过二分计算st[x]和ed[x]为x最左和最右到哪,然后用链表记录ed[x]为某个值的点有哪些,

对于ed[x]=i的点线段树[1,st[x]-1]区间加w[x],因为这些点右面不能被覆盖,左面再不能的话就要补偿了

复杂度 k*n*logn,区间加n次,区间min也有n次

注意:

1.j==1的时候O(n)特判就行了

2.n++ k++后 d[n]=w[n]=INF c[n]=0,f[n]就是最优解了

//
// main.cpp
// bzoj1835
//
// Created by Candy on 2017/1/8.
// Copyright © 2017年 Candy. All rights reserved.
// #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define lc o<<1
#define rc o<<1|1
#define m ((l+r)>>1)
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
const int N=,INF=1e9+;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int n,k,d[N],c[N],s[N],w[N];
int st[N],ed[N],f[N]; struct edge{
int v,ne;
}e[N];
int h[N],cnt;
inline void ins(int u,int v){
cnt++;
e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
} struct node{
int mn,tag;
}t[N<<];
inline void merge(int o){t[o].mn=min(t[lc].mn,t[rc].mn);}
inline void paint(int o,int d){
t[o].tag+=d;
t[o].mn+=d;
}
inline void pushDown(int o){
if(t[o].tag){
paint(lc,t[o].tag);
paint(rc,t[o].tag);
t[o].tag=;
}
}
void build(int o,int l,int r){
t[o].tag=;
if(l==r) t[o].mn=f[l];
else{
build(lson);
build(rson);
merge(o);
}
}
void segAdd(int o,int l,int r,int ql,int qr,int d){
if(ql>qr) return;
if(ql<=l&&r<=qr) paint(o,d);
else{
pushDown(o);
if(ql<=m) segAdd(lson,ql,qr,d);
if(m<qr) segAdd(rson,ql,qr,d);
merge(o);
}
}
int segQue(int o,int l,int r,int ql,int qr){
if(ql>qr) return ;
if(ql<=l&&r<=qr) return t[o].mn;
else{
pushDown(o);
int mn=INF;
if(ql<=m) mn=min(mn,segQue(lson,ql,qr));
if(m<qr) mn=min(mn,segQue(rson,ql,qr));
return mn;
}
} void dp(){
int ans=INF,_=;
for(int i=;i<=n;i++){
f[i]=_+c[i];
for(int k=h[i];k;k=e[k].ne)
_+=w[e[k].v];
//printf("f j1 %d\n",f[i]);
} for(int j=;j<=k;j++){
build(,,n);
for(int i=;i<=n;i++){
f[i]=segQue(,,n,,i-)+c[i];
for(int k=h[i];k;k=e[k].ne){
int v=e[k].v;
segAdd(,,n,,st[v]-,w[v]);
}
}
ans=min(ans,f[n]);
}
printf("%d",ans);
}
int main(int argc, const char * argv[]) {
n=read();k=read();
for(int i=;i<=n;i++) d[i]=read();
for(int i=;i<=n;i++) c[i]=read();
for(int i=;i<=n;i++) s[i]=read();
for(int i=;i<=n;i++) w[i]=read();
n++;k++;
d[n]=INF;w[n]=INF;
for(int i=;i<=n;i++){
st[i]=lower_bound(d+,d++n,d[i]-s[i])-d;
ed[i]=lower_bound(d+,d++n,d[i]+s[i])-d;
if(d[ed[i]]-d[i]>s[i]) ed[i]--;
ins(ed[i],i);
//printf("sted %d %d %d\n",i,st[i],ed[i]);
}
dp();
return ;
}

BZOJ 1835: [ZJOI2010]base 基站选址 [序列DP 线段树]的更多相关文章

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

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

  2. 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] & ...

  3. BZOJ 1835 [ZJOI2010]base 基站选址:线段树优化dp

    传送门 题意 有 $ n $ 个村庄在一排直线上,现在要建造不超过 $ K $ 个通讯基站,基站只能造在村庄处. 第 $ i $ 个村庄距离第 $ 1 $ 个村庄的距离为 $ D_i $ .在此建造基 ...

  4. bzoj 1835: [ZJOI2010]base 基站选址

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

  5. bzoj[1835][ZJOI2010]base 基地选址

    bzoj[1835][ZJOI2010]base 基地选址 标签: 线段树 DP 题目链接 题解 这个暴力DP的话应该很容易看出来. dp[i][j]表示造了i个通讯站,并且j是第i个的最小费用. \ ...

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

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

  7. bzoj 1835/luogu P2605 : [ZJOI2010]base 基站选址

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

  8. BZOJ1835: [ZJOI2010]base 基站选址【线段树优化DP】

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

  9. bzoj1835[ZJOI2010]base基站选址

    据说正解是什么线段树优化DP,但是作为脑子有坑选手,我们需要5k的做法: 主席树+决策单调性..... F[m][i]表示已经放置了m个基站,第m个基站放置在第i个村庄,第i个村庄及之前的村庄的总最少 ...

随机推荐

  1. 让USB键盘的LED灯听你的!(不干扰使用)

    最近在研究一个课题,如何能利用键盘的led灯通道进行有效通信,那么首先要做的就是尝试能否在不影响键盘的情况下控制LED灯(num lock ,caps lock ,scroll lock)的使用. 首 ...

  2. html本地存储尝试

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. JavaScript学习总结【1】、初识JS

    1.什么是 JavaScript? JavaScript 是一门跨平台.面向对象的动态的弱类型的轻量级解释型语言,是一种基于对象和事件驱动并具有相对安全性的客户端脚本语言.应用于 HTML 文档能够在 ...

  4. js 中的流程控制-循环(for)语句

    for语句: <script> /* for(exp1;exp2;exp3){ 循环体; } exp1:无条件的执行第一个表达式 exp2:判断是否能执行循环体的条伯 exp3:做增量的操 ...

  5. Android PackageManager packages.xml文件格式

    packages.xml文件存放在/data/system目录下    该文件记录了系统中所有应用程序的包管理相关信息    PmS根据该文件进行包管理的各种操作 标签名称 所包含的值举例 last- ...

  6. HTTP 无法注册 URL http://+:80/Temporary_Listen_Addresses/92819ef8-81ea-4bd9-

    今天在练习wcf时,客户端调用服务端方法时出现异常.如下: 未处理System.ServiceModel.AddressAlreadyInUseException Message="HTTP ...

  7. 用Python实现的一个简单的爬取省市乡镇的行政区划信息的脚本

    # coding=utf-8 # Creeper import os import bs4 import time import MySQLdb import urllib2 import datet ...

  8. CentOS 6.0 图文安装教程

    CentOS 6.0下载地址:wget http://ftp.riken.jp/Linux/centos/6.0/isos/i386/CentOS-6.0-i386-bin-DVD.iso 下边就是安 ...

  9. 全连接的BP神经网络

    <全连接的BP神经网络> 本文主要描述全连接的BP神经网络的前向传播和误差反向传播,所有的符号都用Ng的Machine learning的习惯.下图给出了某个全连接的神经网络图. 1前向传 ...

  10. BZOJ 1002 轮状病毒

    Description 给定n(N<=100),编程计算有多少个不同的n轮状病毒. Input 第一行有1个正整数n. Output 将编程计算出的不同的n轮状病毒数输出 Sample Inpu ...