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

标签: 线段树 DP


题目链接

题解

这个暴力DP的话应该很容易看出来。

dp[i][j]表示造了i个通讯站,并且j是第i个的最小费用。

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

这个是\(O(n^2k)\)的,很明显我们要对其进行优化。

首先i这一维可以滚掉。

而其实麻烦之处就是cost(i,j)这里,我们肯定不能对其进行预处理。只能够利用其本身的一些性质来做。

我们发现,每个人不需要补偿时是在一段区间内的。可以直接先二分这段区间预处理出来。

然后把这些区间按照右端点排序,每扫到右端点就把左端点之前的加上这个区间的值(补偿值)。

具体实现,由于右端点的值域是\([1,n]\),我们可以沿用一下存邻接表的那套理论。

区间加值的话,用线段树维护一下就好。

感觉线段树实现过程讲的不太清,总结一下算法步骤:

1.每次线段树设为上一次dp的结果(因为要用这个更新啊)。

2.dp[j]直接是线段树中询问[1,j-1] + c[j]。

3.维护补偿的值,在把右端点为j的在线段树中更新[1,左端点-1]。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define REP(i,a,b) for(int i=(a),_end_=(b);i<=_end_;i++)
#define DREP(i,a,b) for(int i=(a),_end_=(b);i>=_end_;i--)
#define EREP(i,a) for(int i=start[(a)];i;i=e[i].next)
inline int read()
{
int sum=0,p=1;char ch=getchar();
while(!(('0'<=ch && ch<='9') || ch=='-'))ch=getchar();
if(ch=='-')p=-1,ch=getchar();
while('0'<=ch && ch<='9')sum=sum*10+ch-48,ch=getchar();
return sum*p;
} const int maxn=20020; int n,k,w[maxn],d[maxn],cost[maxn],s[maxn];
int st[maxn],ed[maxn];
vector <int>g[maxn];
const int inf=0x3f3f3f3f;
void init()
{
n=read();k=read();
REP(i,2,n)d[i]=read();
REP(i,1,n)cost[i]=read();
REP(i,1,n)s[i]=read();
REP(i,1,n)w[i]=read();
n++;k++;
w[n]=d[n]=inf;
s[n]=cost[n]=0;
REP(i,1,n)
{
st[i]=lower_bound(d+1,d+n+1,d[i]-s[i])-d;
ed[i]=lower_bound(d+1,d+n+1,d[i]+s[i])-d;
if(d[ed[i]]>d[i]+s[i])ed[i]--;
g[ed[i]].push_back(i);
}
} struct node {
int mn,lz;
void Merge(node a,node b)
{
mn=min(a.mn,b.mn);
}
};
node c[maxn*4];
int dp[maxn]; #define lc (o<<1)
#define rc (o<<1 | 1)
#define left lc,l,mid
#define right rc,mid+1,r void make_tree(int o,int l,int r)
{
c[o].lz=0;
if(l==r)
{
c[o].mn=dp[l];
return;
}
int mid=(l+r)>>1;
make_tree(left);
make_tree(right);
c[o].Merge(c[lc],c[rc]);
} inline void push_down(int o,int l,int r)
{
if(c[o].lz)
{
c[lc].lz+=c[o].lz;c[rc].lz+=c[o].lz;
c[lc].mn+=c[o].lz;c[rc].mn+=c[o].lz;
c[o].lz=0;
}
} void update(int ql,int qr,int x,int o,int l,int r)
{
if(ql>qr)return;
if(ql<=l && r<=qr)
{
c[o].mn+=x;
c[o].lz+=x;
return;
}
push_down(o,l,r);
int mid=(l+r)>>1;
if(ql<=mid)update(ql,qr,x,left);
if(qr>mid)update(ql,qr,x,right);
c[o].Merge(c[lc],c[rc]);
} int query(int ql,int qr,int o,int l,int r)
{
if(ql>qr)return 0;
if(ql<=l && r<=qr)
{
return c[o].mn;
}
push_down(o,l,r);
int mid=(l+r)>>1,ans=inf;
if(ql<=mid)ans=min(ans,query(ql,qr,left));
if(qr>mid)ans=min(ans,query(ql,qr,right));
return ans;
} int ans=inf; void doing()
{
int sum=0;
REP(j,1,n)
{
dp[j]=sum+cost[j];
REP(l,0,g[j].size()-1)
{
int x=g[j][l];
sum+=w[x];
}
}
ans=min(ans,dp[n]); REP(i,2,k)
{
make_tree(1,1,n);
//REP(j,1,i)dp[j]=dp[j-1]+cost[j];
REP(j,1,n)
{
dp[j]=query(1,j-1,1,1,n)+cost[j];
//update(j,j,dp[j],1,1,n);
REP(l,0,g[j].size()-1)
{
int x=g[j][l];
update(1,st[x]-1,w[x],1,1,n);
}
}
ans=min(ans,dp[n]);
}
printf("%d\n",ans);
} int main()
{
freopen("base.in","r",stdin);
freopen("base.out","w",stdout);
init();
doing();
return 0;
}

bzoj[1835][ZJOI2010]base 基地选址的更多相关文章

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

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

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

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

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

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

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

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

    传送门 题意 有 $ n $ 个村庄在一排直线上,现在要建造不超过 $ K $ 个通讯基站,基站只能造在村庄处. 第 $ i $ 个村庄距离第 $ 1 $ 个村庄的距离为 $ D_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. bzoj 1835: [ZJOI2010]基站选址

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

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

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

随机推荐

  1. 在iOS App 中添加启动画面

    你可以认为你需要为启动画面编写代码,然而Apple 让你可以非常简单地在Xcode中完成.不需要编写代码,你仅需要在Xcode中进行一些配置. 1.什么是启动画面(Splash Screen)? 启动 ...

  2. vue源码入口文件分析

    开发vue项目有段时间了, 之前用angularjs 后来用 reactjs 但是那时候一直没有时间把自己看源码的思考记录下来,现在我不想再浪费这 来之不易的思考, 我要坚持!! 看源码我个人感觉非常 ...

  3. Jquery如何删除table里面checkbox选中的多个行

    思路:遍历被选中的checkbox对象→根据选中项筛选出需要删除的行→删除行.实例说明如下: 1.HTML结构 <table id = "test_table"> &l ...

  4. MySql 修改外键 支持级联删除

    首先必须要有外键,InnoDB甚么的都不说了,直接上修改句子. 要先删除该外键,然后添加. 具体原因貌似是因为不支持alter外键的操作. ALTER TABLE `t_terminal` DROP ...

  5. cookie sessionStorage localStorage 之间的关系

    先说一个cookie 因为HTTP是无状态的 所以cookie诞生 用于保存会话信息 大小 4096b 一般在4095b以内 数量限制 20 -50 根据浏览器不同 操作的是一个字符串 可以设置参数 ...

  6. python_如何对字典进行排序?

    案例: 某班英语成绩以字典的形式存储为: {'lili':78, 'jin':50, 'liming': 30, ......} 依据成绩高低,进行学生成绩排名 如何对字典排序? 方法1: #!/us ...

  7. python_如何在列表、字典中筛选数据?

    实际问题有哪些? 过滤掉列表[3,9,-1,10.-2......] 中负数 筛选出字典{'li_ming':90,'xiao_hong':60,'li_kang':95,'bei_men':98} ...

  8. junit4X系列--Exception

    原文出处:http://www.blogjava.net/DLevin/archive/2012/11/02/390684.html.感谢作者的无私分享. 说来惭愧,虽然之前已经看过JUnit的源码了 ...

  9. Ajax 基础笔记

    Ajax内容: 同步交互与异步交互 同步交互:客户端向服务器端发送请求,服务器端向客户端进行响应,这个过程中客户端不能做其他事情 异步交互:客户端向服务器端发送请求,服务器端向客户端进行响应,这个过程 ...

  10. 聊聊js里面容易忽视的一些东西(1)

      JavaScript对象的创建方式 在JavaScript中,创建对象的方式包括两种:对象字面量和使用new表达式.对象字面量是一种灵活方便的书写方式,例如: 1 2 3 4 5 6 var o1 ...