【DP】【线段树】基站选址(luogu 2605/金牌导航 数据结构优化DP-2)
正題
luogu 2605
金牌導航 數(shù)據(jù)結(jié)構(gòu)優(yōu)化DP-2
題目大意
有若干個村莊在一條直線上,距離第一個村莊did_idi?,在該村莊建立基站要花費cic_ici?,如果在離該村不大于sis_isi?的范圍內(nèi)有一個基站,那么該村會被信號覆蓋,如果一個村莊沒有被信號覆蓋,那么有wiw_iwi?的代價
現(xiàn)在最多建k個基站,問你最小代價
解題思路
設fi,jf_{i,j}fi,j?為在第i個村莊建建第j個基站的費用
那么有fi,j=mink=1i?1(fk,j?1+costk,i)f_{i,j}=min_{k=1}^{i-1}(f_{k, j - 1} + cost_{k, i})fi,j?=mink=1i?1?(fk,j?1?+costk,i?),cost為在k,i建基站中間覆蓋不到的村莊的代價
第二維可以在外面枚舉而省掉,那么有fi=mink=1i?1(fk+costk,i)f_i=min_{k=1}^{i-1}(f_k + cost_{k, i})fi?=mink=1i?1?(fk?+costk,i?)
現(xiàn)在考慮計算cost的優(yōu)化
可以先預處理出sti,edist_i,ed_isti?,edi?,分別為可以遍歷到i的點中最左/右的,這可以用二分實現(xiàn)
計算完后在edied_iedi?建立a數(shù)組,把i丟進去,得到的就是以x(i?x)x(i\leqslant x)x(i?x)為最后一個可以覆蓋到的該點的點,就是x可以覆蓋i,而x+1覆蓋不到i
當我們計算完fif_ifi?時,由于后面的點無法遍歷到a里面的點,所以如果從stk(k∈a)st_k(k\in a)stk?(k∈a)前面的點轉(zhuǎn)移到后面,那么k點就被覆蓋到,就要加上wkw_kwk?,所以對[1, st_k-1]加上wkw_kwk?
因為fif_ifi?的上一個基站在[1,i-1]內(nèi),所以在[1,i-1]中找最小值即可
找最小值得這個過程可以用線段樹來實現(xiàn)
對于求解答案,可以在末尾再加一個點,該點w=inf,d=inf,c=0,s=0w=inf,d=inf,c=0,s=0w=inf,d=inf,c=0,s=0,然后使建基站的最大數(shù)量+1,然后在該點找答案即可
代碼
#include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define N 20021 using namespace std; int n, m, ans, d[N], c[N], s[N], w[N], f[N], st[N], ed[N]; vector<int>a[N]; struct Tree {int s[N<<2], lazy[N<<2];#define ls x*2#define rs x*2+1void up(int x){s[x] = min(s[ls], s[rs]);return;}void down(int x){if (lazy[x]){lazy[ls] += lazy[x];lazy[rs] += lazy[x];s[ls] += lazy[x];s[rs] += lazy[x];lazy[x] = 0;}return;}void build(int x, int l, int r){lazy[x] = 0;if (l == r){s[x] = f[l];return;}int mid = l + r >> 1;build(ls, l, mid);build(rs, mid + 1, r);up(x);return;}void change(int x, int L, int R, int l, int r, int y){if (l > r) return;if (L == l && R == r){lazy[x] += y;s[x] += y;return;}int mid = L + R >> 1;down(x);if (r <= mid) change(ls, L, mid, l, r, y);else if(l > mid) change(rs, mid + 1, R, l, r, y);else change(ls, L, mid, l, mid, y), change(rs, mid + 1, R, mid + 1, r, y);up(x);return;}int ask(int x, int L, int R, int l, int r){if (l > r) return 0;if (L == l && R == r) return s[x];int mid = L + R >> 1;down(x);if (r <= mid) return ask(ls, L, mid, l, r);else if (l > mid) return ask(rs, mid + 1, R, l, r);else return min(ask(ls, L, mid, l, mid), ask(rs, mid + 1, R, mid + 1, r));} }T; int main() {scanf("%d%d", &n, &m);for (int i = 2; i <= n; ++i) scanf("%d", &d[i]);for (int i = 1; i <= n; ++i) scanf("%d", &c[i]);for (int i = 1; i <= n; ++i) scanf("%d", &s[i]);for (int i = 1; i <= n; ++i) scanf("%d", &w[i]);n++;m++;d[n] = w[n] = 2000000001;for (int i = 1; i <= n; ++i){st[i] = lower_bound(d + 1, d + 1 + n, d[i] - s[i]) - d;//計算st,eded[i] = upper_bound(d + 1, d + 1 + n, d[i] + s[i]) - d - 1;a[ed[i]].push_back(i);}int g = 0;for (int i = 1; i <= n; ++i)//前面沒有基站{f[i] = g + c[i];for (int j = 0; j < a[i].size(); ++j)g += w[a[i][j]];}ans = f[n];for (int k = 1; k <= m; ++k){T.build(1, 1, n);for (int i = 1; i <= n; ++i){f[i] = T.ask(1, 1, n, 1, i - 1) + c[i];for (int j = 0; j < a[i].size(); ++j)T.change(1, 1, n, 1, st[a[i][j]] - 1, w[a[i][j]]);//后面的點遍歷不到}ans = min(ans, f[n]);}printf("%d", ans);return 0; }總結(jié)
以上是生活随笔為你收集整理的【DP】【线段树】基站选址(luogu 2605/金牌导航 数据结构优化DP-2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片放大后模糊怎么变清晰图片放大后很模糊
- 下一篇: 大一新生有必要买电脑吗机械大一新生有必要