洛谷 P1792 种树
思路
我们考虑单个树坑,若此树坑被选择,则两侧树坑都不可选;
会存在某种情况,选择两侧的树坑带来的总收益大于此树坑,我们就可以在选择此树坑时向优先队列中加入“后悔项”,权重是其左右两侧的树坑之和减去该树坑(v[i−1]+v[i+1]−v[i]);
普适来说,我们构造一种“评价块”,其满足以下性质:
- 其长度为奇数;
- 其左右端点树坑均认为被种植,并以此隔一种一;
- 其权值为区间内所有被种植-未种植,即(∑i被种植vi−∑j未种植vj);
- 其区间内所有子评价块均不独立参与优先队列;
- 任意评价块被贪心接受后,都使得总种植数+1;
- 所有独立的评价块的并集为整个环,交集为空;
我们对于这样的评价块,采取这样的策略:
- 初始化每个树坑为单独的评价块,权值为树坑的美观值,加入优先队列进行贪心;
- 若队顶评价块不独立,则抛出,直至队顶树坑独立;
- 将独立的队顶评价块吸收,并建立对应的新评价块(后悔快),新块的范围为本块及左右两相邻块,权值为相邻块权值之和减去本块,并做好不独立和相邻标记;
如此循环 m 次即可;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll N = 1e7; const ll M = 1e9 + 7; struct prt { int v, tl, tr; } p[200005]; bool vis[200005]; priority_queue<pair<int, int>> pue; int main() { int n, m, ans = 0; cin >> n >> m; if (n < m * 2) { printf("Error!"); return 0; } for (int i = 1; i <= n; i++) { scanf("%d", &p[i].v); p[i].tr = i + 1; p[i].tl = i - 1; pue.push({p[i].v, i}); } p[1].tl = n, p[n].tr = 1; for (int i = 1; i <= m; i++) { while (vis[pue.top().second]) pue.pop(); pair<int, int> now = pue.top(); pue.pop(); ans += now.first; vis[p[now.second].tl] = vis[p[now.second].tr] = 1; p[now.second].v = p[p[now.second].tl].v + p[p[now.second].tr].v - p[now.second].v; p[now.second].tl = p[p[now.second].tl].tl; p[now.second].tr = p[p[now.second].tr].tr; p[p[now.second].tl].tr = now.second; p[p[now.second].tr].tl = now.second; pue.push({p[now.second].v, now.second}); } cout << ans; }
|
ED
\