1835: [ZJOI2010]base 基站选址

68 篇文章 0 订阅
11 篇文章 0 订阅

1835: [ZJOI2010]base 基站选址

Time Limit: 100 Sec   Memory Limit: 64 MB
Submit: 1188   Solved: 558
[ Submit][ Status][ Discuss]

Description

有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。

Input

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

Output

3 2 1 2 2 3 2 1 1 0 10 20 30

Sample Input

4

Sample Output

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

HINT

Source

[ Submit][ Status][ Discuss]

强烈的dp即视感???
f[i][j]:最后一个基站建在第i个村庄,已经建了j个基站,不考虑i号村庄右端的村庄,最少代价
(为什么不考虑右边呢??转移方便啊。。。)
写出方程f[i][j] = min{f[k][j-1] + cost(k,i)} + c[i]
cost(i,j):在i号和j号建基站,求出区间(i,j)内未被覆盖的基站需要赔偿的总钱数
暴力转移时O(n^2k)的
难点在于如何求出cost函数???
把这个问题拆成两部分思考
区间(i,j)内的村庄何时需要赔偿???
显然,对于村庄k,它与i或者j其中一个距离不超过s[k]的时候他就不用被赔偿
换句话说,我们可以预处理出L[k]R[k],表示对于k,最左边L[k]最右边R[k]有基站时,他不用被赔偿
那么是否赔偿的关键就是取决于L[k]和R[k]了
假设我们从左往右慢慢推状态
对于f[i][j],不考虑i右边的村庄
随着i的右移,之前能够被i覆盖的村庄(R[k]>=i)可能就没办法被i+1覆盖,以及我们要考虑一些新进来的位置
对于第一种情况,总有一个i使得R[k]<i,我们维护一个堆,每次取堆顶看看,如果R[k]<i,那些转移会受影响?我们预处理了L数组,那么L[k]左边的所有状态转移时就都要加上w[k],区间加法??
线段树直接解决,既然用了线段树,区间查询也不是难事,转移就解决了!
第二种情况,,扔堆里呀

线段树开小WA了一发。。。。
#define priority_queue pq
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector> 
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 2E4 + 10;
const int T = 8;
const int INF = ~0U>>1;

struct data{
	int posi,Num;
	data(){}
	data(int posi,int Num): posi(posi),Num(Num){}
	bool operator < (const data &b) const {
		return posi > b.posi;	
	}
};
typedef __gnu_pbds::pq<data,less<data>,__gnu_pbds::pairing_heap_tag> Heap;

int n,k,dis[maxn],w[maxn],v[maxn],L[maxn],R[maxn],
	Add[maxn*T],Min[maxn*T],f[maxn][110],pay[maxn];

struct d2{
	int posi,Num;
	d2(){}
	d2(int posi,int Num): posi(posi),Num(Num){}
	bool operator < (const d2 &b) const {
		return posi < b.posi;	
	}
};
typedef __gnu_pbds::pq<d2,less<d2>,__gnu_pbds::pairing_heap_tag> Heap2;

void pushdown(int o)
{
	if (!Add[o]) return;
	Min[o] += Add[o];
	Add[o<<1] += Add[o];
	Add[o<<1|1] += Add[o];
	Add[o] = 0;
}

void maintain(int o)
{
	pushdown(o<<1);
	pushdown(o<<1|1);
	Min[o] = min(Min[o<<1],Min[o<<1|1]);
}

void Insert(int o,int l,int r,int pos,int key)
{
	pushdown(o);
	maintain(o);
	if (l == r) {
		Min[o] += key;
		return;
	}
	int mid = (l + r) >> 1;
	if (pos <= mid) Insert(o<<1,l,mid,pos,key);
	else Insert(o<<1|1,mid+1,r,pos,key);
	Min[o] = min(Min[o<<1],Min[o<<1|1]);
}

void Modify(int o,int l,int r,int ml,int mr,int key)
{
	if (ml > mr) return;
	pushdown(o);
	if (ml <= l && r <= mr) {
		Add[o] += key;
		return;
	}
	int mid = (l + r) >> 1;
	if (ml <= mid) Modify(o<<1,l,mid,ml,mr,key);
	if (mr > mid) Modify(o<<1|1,mid+1,r,ml,mr,key);
	maintain(o);
}

int Query(int o,int l,int r,int ql,int qr)
{
	if (ql > qr) return 0;
	pushdown(o);
	if (ql <= l && r <= qr) return Min[o];
	int ret = ~0U>>1,mid = (l + r) >> 1;
	if (ql <= mid) ret = Query(o<<1,l,mid,ql,qr);
	if (qr > mid) ret = min(ret,Query(o<<1|1,mid+1,r,ql,qr));
	return ret;
}

int getint()
{
	char ch = getchar();
	int ret = 0;
	while (ch < '0' || '9' < ch) ch = getchar();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

void Clear() 
{
	memset(Min,0,sizeof(Min));
	memset(Add,0,sizeof(Add));
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
	#endif
	
	n = getint(); k = getint();
	int Ans = 0;
	for (int i = 2; i <= n; i++)
		dis[i] = getint();
	for (int i = 1; i <= n; i++)
		v[i] = getint();
	for (int i = 1; i <= n; i++) {
		int s = getint();
		int tmp = dis[i] - s;
		int pos = lower_bound(dis + 1,dis + n + 1,tmp) - dis;
		L[i] = pos;
		tmp = dis[i] + s;
		pos = lower_bound(dis + 1,dis + n + 1,tmp) - dis;
		if (dis[pos] > tmp) --pos;
		R[i] = pos;
	}
	for (int i = 1; i <= n; i++)
		w[i] = getint(),Ans += w[i];
		
	for (int j = 1; j <= k; j++) {
		Clear(); Heap Q;
		for (int i = 1; i <= n; i++) {
			if (i < j) {
				if (i == j - 1) 
					Insert(1,0,n,i,f[i][j-1]);
				continue;
			}
			while (!Q.empty()) {
				data K = Q.top();
				if (K.posi < i) {
					if (j == 1) Modify(1,0,n,0,n,w[K.Num]);
					else Modify(1,0,n,1,L[K.Num] - 1,w[K.Num]);
					Q.pop();
				}
				else break;
			}
			f[i][j] = Query(1,0,n,j-1,i-1) + v[i];
			Q.push(data(R[i],i));
			Insert(1,0,n,i,f[i][j-1]);
		}
	}
	
	Heap2 Q;
	int tot = 0;
	for (int i = n; i; i--) {
		while (!Q.empty()) {
			d2 K = Q.top();
			if (K.posi > i) {
				tot += w[K.Num];
				Q.pop();
			} 
			else break;
		}
		pay[i] = tot;
		Q.push(d2(L[i],i));
	}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= k; j++) {
			if (j > i) continue;
			Ans = min(Ans,f[i][j] + pay[i]);
		}
	cout << Ans;
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值