【YBTOJ】【Luogu P2605】[ZJOI2010]基站选址

链接:

题目

题目大意:

\(n\) 个点里建不超过 \(k\) 个基站,每个点的位置是 \(d_i\),在 \(i\) 建基站的钱是 \(c_i\),如果在 \(i\) 不超过 \(s_i\) 的范围内没有基站,就要再付 \(w_i\)。求最小费用。

正文:

考虑动态规划。设 \(f_{i,j}\) 表示在第 \(i\) 个点建第 \(j\) 个基站的最小费用。则有:

\[f_{i,j}=\min_{k<i}\{f_{k,j-1}+cost_{k,i}\} \]

其中 \(cost_{k,i}\) 表示从 \(k\)\(i\) 没有覆盖到的点的 \(w\) 值总和。我们的主要问题,就是求解这个 \(cost_{k,i}\)

先二分出 \(i\) 的范围 \(l_i,r_i\)。那么若在 \(j\) 点建基站,有 \(r_i<d_j\) 的话费用就要加上 \(w_i\),这一步可以用邻接表实现。

还有一种情况就是,从 \([1,l_i)\) 转移过来的话,要费用要加上 \(w_i\)。那么可以用线段树实现。

由于在转移过程中总是是从 \(j-1\) 过来的,那么可以滚动数组。

代码:

int n, k;
ll f[N];

struct SegmentTree
{
	struct node
	{
		int l, r;
		ll val, lazy;
	} t[N << 2];
	
	void build(int p, int l, int r)
	{
		t[p].l = l, t[p].r = r;
		t[p].lazy = 0;
		if (t[p].l == t[p].r)
		{
			t[p].val = f[l];
			return ;
		}
		int mid = t[p].l + t[p].r >> 1;
		build (p << 1, l, mid);
		build (p << 1 | 1, mid + 1, r);
		t[p].val = min(t[p << 1].val, t[p << 1 | 1].val);
	}
	
	void spread(int p)
	{
		if (!t[p].lazy) return;
		t[p << 1].val += t[p].lazy;
		t[p << 1].lazy += t[p].lazy;
		t[p << 1 | 1].val += t[p].lazy;
		t[p << 1 | 1].lazy += t[p].lazy;
		t[p].lazy = 0;
	}
	
	void modify(int p, int l, int r, ll val)
	{
		if (l <= t[p].l && t[p].r <= r)
		{
			t[p].val += val;
			t[p].lazy += val;
			return ;
		}
		spread(p);
		int mid = t[p].l + t[p].r >> 1;
		if (l <= mid) modify (p << 1, l, r, val);
		if (mid < r) modify (p << 1 | 1, l, r, val);
		t[p].val = min(t[p << 1].val, t[p << 1 | 1].val);
	}
	
	ll query(int p, int l, int r)
	{
		if (l <= t[p].l && t[p].r <= r)
			return t[p].val;
		spread(p);
		int mid = t[p].l + t[p].r >> 1;
		ll ans = 1ll << 30;
		if (l <= mid) ans = min (ans, query (p << 1, l, r));
		if (mid < r) ans = min (ans, query (p << 1 | 1, l, r));
		t[p].val = min(t[p << 1].val, t[p << 1 | 1].val);
		return ans;
	}
}t;

ll s[N], c[N], d[N], w[N], l[N], r[N];

struct edge
{
	int to, nxt;
}e[N];
int head[N], tot;
void add(int u, int v) {e[++tot] = (edge){v, head[u]}, head[u] = tot;} 

int main()
{
	scanf ("%d%d", &n, &k);
	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++; d[n] = w[n] = 0x3f3f3f3f;
	
	for (int i = 1; i <= n; i++)
	{
		l[i] = lower_bound(d + 1, d + n + 1, d[i] - s[i]) - d;
		r[i] = lower_bound(d + 1, d + n + 1, d[i] + s[i]) - d;
		if (d[i] + s[i] < d[r[i]]) r[i]--;
		add(r[i], i);
	}
	ll sum = 0;
	for (int i = 1; i <= n; i++)
	{
		f[i] = sum + c[i];
		for (int j = head[i]; j; j = e[j].nxt) 
			sum += w[e[j].to];
	}
	ll ans = f[n];
	for (int j = 2; j <= k + 1; j++)
	{
		t.build(1, 1, n);
		for (int i = 1; i <= n; i++)
		{
			f[i] = (i >= j? t.query(1, j - 1, i - 1): 0) + c[i];
			for (int g = head[i]; g; g = e[g].nxt)
				if(l[e[g].to] > 1) t.modify(1, 1, l[e[g].to] - 1, w[e[g].to]);
		}
		ans = min(ans, f[n]);
	}
	printf ("%lld\n", ans);
    return 0;
}
posted @ 2021-01-25 08:19  Jayun  阅读(76)  评论(0编辑  收藏  举报