基站选址(base.c/cpp/pas)

题目描述 

N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di。需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci。如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了。如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi。现在的问题是,选择基站的位置,使得总费用最小。

输入

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

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

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

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

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

输出

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

样例输入


1 2
2 3 2
1 1 0
10 20 30

样例输出

4

提示

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

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

solution

先列出DP式

f[i][j]表示当前建到i(i必建),已经建了j个的最小代价

f[i][j]=f[k][j-1]+cost(k+1,i-1)+c[i];

效率O(n^3)

因为j只和j-1有关,我们可以先枚举j,对于每一个j,考虑优化cost(k+1,i-1):

令l[i]为最左的能覆盖i的基站的位置,r[i]同理

用线段树存1~i-1  f[k][j-1]+cost(k+1,i-1 ) 的值

处理完i,将要加入i+1时对于r[x]=i的点显然无法被从右边覆盖,那么将1~l[x]-1加上w[x],

也就是如果f[i+1]由f[k]转移来,且k<l[x],那么x就不会被覆盖了,cost要加上w[x].

线段树维护区间加,单点查

效率O(nlogn)

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#define maxn 200005
#define inf 900000000
using namespace std;
int n,k,dp[maxn],d[maxn],c[maxn],s[maxn],w[maxn],lm[maxn],rm[maxn];
int x,tot,head[maxn];
struct node{
int nex,v;
}e[maxn*2];
struct no
{
int l,r,x,bj;
}tree[maxn*4];
void lj(int t1,int t2)
{
e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void get(int k)
{
int l=1,r=k;
x=d[k];
while(l<r)
{
int mid=(l+r)/2;
if(x-d[mid]<=s[k])r=mid;
else l=mid+1;
}
lm[k]=l;
l=k,r=n;
while(l<r)
{
int mid=(l+r+1)/2;
if(d[mid]-x<=s[k])l=mid;
else r=mid-1;
}
rm[k]=l;
lj(l,k);
}
void wh(int k)
{
tree[k].x=min(tree[k*2].x,tree[k*2+1].x);
}
void build(int k,int L,int R)
{
tree[k].l=L,tree[k].r=R;tree[k].bj=0;
if(L==R){
tree[k].x=dp[L];
return;
}
int mid=(L+R)/2;
build(k*2,L,mid);build(k*2+1,mid+1,R);
wh(k);
}
void down(int k)
{
if(tree[k].bj>0)
{
tree[k*2].bj+=tree[k].bj;tree[k*2+1].bj+=tree[k].bj;
tree[k*2].x+=tree[k].bj;tree[k*2+1].x+=tree[k].bj;
tree[k].bj=0;
}
}
int ask(int k,int L,int R)
{
if(L>R)return 0;
down(k);
if(tree[k].l>=L&&tree[k].r<=R)
{
return tree[k].x;
}
int mid=(tree[k].l+tree[k].r)/2;
int u=inf;
if(L<=mid)u=min(u,ask(k*2,L,R));
if(R>mid)u=min(u,ask(k*2+1,L,R));
return u;
}
void lian(int k,int L,int R,int v)
{
if(L>R)return;
down(k);
if(tree[k].l>=L&&tree[k].r<=R)
{ tree[k].bj+=v;
tree[k].x+=v;
return;
}
int mid=(tree[k].l+tree[k].r)/2;
if(L<=mid)lian(k*2,L,R,v);
if(R>mid)lian(k*2+1,L,R,v);
wh(k);
}
int ss()
{
char ch;int v=0;
while(!isdigit(ch=getchar()));v=v+ch-'0';
while(isdigit(ch=getchar()))v=(v<<1)+(v<<3)+ch-'0';
return v;
}
int main()
{
n=ss();k=ss();
for(int i=2;i<=n;i++)d[i]=ss();
for(int i=1;i<=n;i++)c[i]=ss();
for(int i=1;i<=n;i++)s[i]=ss();
for(int i=1;i<=n;i++)w[i]=ss();
n++;d[n]=inf;
for(int i=1;i<=n;i++)get(i); int tmp=0;
for(int i=1;i<=n;i++){
dp[i]=tmp+c[i];
int p=head[i];
while(p!=0)
{
tmp+=w[e[p].v];
p=e[p].nex;
}
}
int ans=dp[n];
for(int i=2;i<=k+1;i++)
{
build(1,1,n);
for(int j=1;j<=n;j++){ dp[j]=ask(1,1,j-1)+c[j];
int p=head[j];
while(p!=0){
lian(1,1,lm[e[p].v]-1,w[e[p].v]);
p=e[p].nex;
}
}
ans=min(ans,dp[n]);
}
cout<<ans<<endl;
return 0;
}

最新文章

  1. ASCII字符集中的功能/控制字符
  2. 2016HUAS_ACM暑假集训4F - 数论
  3. 内部类--毕向东Java基础教程学习笔记
  4. 在Web api2 中传递复杂参数的一点心得
  5. hdu 3537 Daizhenyang&#39;s Coin (翻硬币游戏)
  6. js正则验证手机号
  7. IOS私人API用法
  8. SRM 590 DIV1
  9. JAVA基本的编程50称号(7-9称号)详细解释
  10. C/C++基础知识总结——继承与派生
  11. java基础知识点---equal,==,hashcode
  12. 《CSS世界》读书笔记(十二)
  13. Testing - 敏捷测试
  14. 怎么停止yum安装并kill进程
  15. js replace替换 忽略大小写问题
  16. 白化(Whitening): PCA 与 ZCA (转)
  17. vue-router 不重新加载问题
  18. Android之Monkey全参数(包含隐藏参数)
  19. 学习笔记一:I2C协议学习和Verilog实现
  20. kernel: swapper: page allocation failure. order:1, mode:0x20

热门文章

  1. Linux nohup 关闭终端的时候,程序依然能在后台运行( linux重定向及nohup不输出的方法)
  2. nuxt.config有关router配置
  3. 39.VUE学习--组件,子组件中data数据的使用,x-template模板的使用,改变for循环里的某条数据里的值
  4. python3 练习题100例 (十九)
  5. python爬虫:利用正则表达式爬取豆瓣读书首页的book
  6. 20145202马超 《Java程序设计》第五周学习总结
  7. Spring---bean的实例化
  8. ImageButton的坑 ImageButton 有问题
  9. Centos7 查看Mysql配置文件
  10. 2.栅格的类中同时设置col-md-* col-sm-*的作用