P2605 [ZJOI2010]基站选址

P2605 [ZJOI2010]基站选址

线段树优化 dp

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

则有:fi,j=mink=1i1(fk,j1+costk,i)f_{i,j}=\min\limits_{k=1}^{i-1}(f_{k,j-1}+cost_{k,i}),其中 costi,jcost_{i,j} 表示 k,ik,i 两点间没有基站的赔偿费用。

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

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

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

sti,edist_i,ed_i 分别表示在区间 [disi,di+si][d_i-s_i,d_i+s_i] 的点的最小及最大编号。

若当前枚举的点 i=edki=ed_k,那么线段树上区间 [1,stk1][1,st_k-1] 加上 wkw_k

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

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

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

具体见代码。

#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;
}
posted @ 2022-05-25 14:08  蒟蒻orz  阅读(2)  评论(0编辑  收藏  举报  来源