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 }

 

posted @ 2016-08-22 21:12  Awner  阅读(251)  评论(0编辑  收藏  举报