JZOJ #4726 种花 (带恢复操作的堆)
题目描述:
在圆周上有$N$个点,每个点有不同的贡献,要求选$M$个点,不能相邻,问最大的贡献。
解题思路:
将所有点扔进一个按贡献的大根堆里,取$M$次,每次取出堆顶$k$。答案加$V_k$,设$k$的前继为$lst$,后继$nxt$,令$V_k=V_{lst}+V_{nxt}-V_k$,再扔进堆里。并令$k$的前继为前继的前继,后继类似。为什么这样呢,新的$k$表示不选$k$这个位置,而取$k$的前继和后继,而这样取的次数不会多或少。那为什么一定是取前继和后继,严格证明我也不会。但手玩一下确实是这样。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 using namespace std; 6 7 const int N = 2e5 + 10; 8 int n, m, a[N], lst[N], nxt[N], ans, mov[N]; 9 10 struct node {int k;}; 11 bool operator < (node x, node y) {return a[x.k] < a[y.k];} 12 priority_queue <node> h; 13 14 int main() { 15 scanf("%d %d", &n, &m); 16 if (n < m * 2) { 17 printf("Error!"); 18 return 0; 19 } 20 for (int i = 1; i <= n; i ++) scanf("%d", &a[i]), h.push((node) {i}); 21 for (int i = 1; i < n; i ++) nxt[i] = i + 1; nxt[n] = 1; 22 for (int i = 2; i <= n; i ++) lst[i] = i - 1; lst[1] = n; 23 while (m --) { 24 node now = h.top(); h.pop(); 25 while (mov[now.k]) { 26 now = h.top(); 27 h.pop(); 28 } 29 ans += a[now.k]; 30 a[now.k] = a[lst[now.k]] + a[nxt[now.k]] - a[now.k]; 31 mov[lst[now.k]] = 1; 32 mov[nxt[now.k]] = 1; 33 lst[now.k] = lst[lst[now.k]], nxt[lst[now.k]] = now.k; 34 nxt[now.k] = nxt[nxt[now.k]], lst[nxt[now.k]] = now.k; 35 h.push((node) {now.k}); 36 } 37 printf("%d", ans); 38 return 0; 39 }