P2605 [ZJOI2010]基站选址

P2605 [ZJOI2010]基站选址

线段树优化 dp

f i , j f_{i,j}fi,j 表示第 i ii 位置建第 j jj 个基站的最小费用。

则有:f i , j = min ⁡ k = 1 i − 1 ( f k , j − 1 + c o s t k , i ) f_{i,j}=\min\limits_{k=1}^{i-1}(f_{k,j-1}+cost_{k,i})fi,j=k=1mini1(fk,j1+costk,i),其中 c o s t i , j cost_{i,j}costi,j 表示 k , i k,ik,i 两点间没有基站的赔偿费用。

另,要在 + ∞ +\infty+ 位置建立一个费用为 0 00,覆盖距离为 0 00 的基站,起到统计答案的作用。

时间复杂度 O ( n 3 k ) \mathcal O(n^3k)O(n3k)

考虑优化,发现 min ⁡ \minmin 即区间最小值,可用线段树优化。

s t i , e d i st_i,ed_isti,edi 分别表示在区间 [ d i − s i , d i + s i ] [d_i-s_i,d_i+s_i][disi,di+si] 的点的最小及最大编号。

若当前枚举的点 i = e d k i=ed_ki=edk,那么线段树上区间 [ 1 , s t k − 1 ] [1,st_k-1][1,stk1] 加上 w k w_kwk

因为后面的点要是从 [ 1 , s t k − 1 ] [1,st_k-1][1,stk1] 的点转移,那么 k kk 就无法被覆盖到,所以要加上赔偿费用。

最后区间查询最小值即可。

时间复杂度 O ( n k log ⁡ n ) \mathcal O(nk \log n)O(nklogn)

具体见代码。

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int _ = 2e4 + 10, inf = 2e18;

int n, k, d[_], c[_], s[_], w[_];

int st[_], ed[_];

vector<int> D[_];

int tr[_ << 2], tag[_ << 2];

int f[_];

void build(int o, int l, int r)
{
	tag[o] = 0;
	if(l == r)
	{
		tr[o] = f[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(o << 1, l, mid);
	build(o << 1 | 1, mid + 1, r);
	tr[o] = min(tr[o << 1], tr[o << 1 | 1]);
}

void update(int o, int l, int r, int L, int R, int val)
{
	if(L > R) return;
	if(L <= l && r <= R)
	{
		tr[o] += val, tag[o] += val;
		return;
	}
	int mid = (l + r) >> 1;
	if(L <= mid) update(o << 1, l, mid, L, R, val);
	if(R > mid) update(o << 1 | 1, mid + 1, r, L, R, val);
	tr[o] = min(tr[o << 1], tr[o << 1 | 1]) + tag[o];
}

int query(int o, int l, int r, int L, int R, int v)
{
	if(L > R) return inf;
	if(L <= l && r <= R)
		return tr[o];
	int mid = (l + r) >> 1, res = 0;
	if(L <= mid) res = query(o << 1, l, mid, L, R, v + tag[o]);
	if(R > mid) res = min(res, query(o << 1 | 1, mid + 1, r, L, R, v + tag[o]));
	return res;
}

signed main()
{
	scanf("%lld%lld", &n, &k);
	d[1] = 0;
	for(int i = 2; i <= n; ++i) scanf("%lld", &d[i]);
	for(int i = 1; i <= n; ++i) scanf("%lld", &c[i]);
	for(int i = 1; i <= n; ++i) scanf("%lld", &s[i]);
	for(int i = 1; i <= n; ++i) scanf("%lld", &w[i]);
	n++, k++;
	d[n] = w[n] = inf;
	for(int i = 1; i <= n; ++i)
	{
		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]--;
		D[ed[i]].push_back(i);
	}
	int now = 0;
	for(int i = 1; i <= n; ++i)
	{
		f[i] = now + c[i];
		for(int j : D[i])
			now += w[j];
	}
	int ans = f[n];
	for(int i = 2; i <= k; ++i)
	{
		build(1, 1, n);
		for(int j = 1; j <= n; ++j)
		{
			f[j] = query(1, 1, n, 1, j - 1, 0) + c[j];
			for(int k : D[j])
				update(1, 1, n, 1, st[k] - 1, w[k]);
		}
		ans = min(ans, f[n]);
	}
	printf("%lld\n", ans);
	return 0;
}

版权声明:本文为qq_46258139原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。