「题解」BZOJ 2151.种树
题目
简化题意
有 \(n\) 围成一个环,环上每个点有一个贡献,你要从环上选 \(m\) 个点(不能相邻)使得选出的点的贡献值的和最大。
思路
贪心 + 链表 + 优先队列。
把贡献都加到大根堆里。
每次取出最大的贡献 \(x\),往队里加入 \(y + z - x\)(\(y,z\) 是在原序列中 \(x\) 两边的贡献)。
不知道为啥对,看到还有用 \(\text{WQS二分}\) 过了的。
Code
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#define MAXN 200001
int n, m, a[MAXN << 1], lt[MAXN << 1], nt[MAXN << 1];
bool used[MAXN << 1];
struct Node {
int w, id;
friend bool operator < (Node n1, Node n2) {
return n1.w < n2.w;
}
};
std::priority_queue<Node> q;
int main() {
//freopen("1.in", "r", stdin);
scanf("%d %d", &n, &m);
if (m > n / 2) {
puts("Error!");
return 0;
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
Node x;
x.w = a[i], x.id = i;
q.push(x);
}
for (int i = 1; i <= n; ++i) {
if (i == 1) lt[i] = n, nt[i] = 2;
else if (i == n) lt[i] = n - 1, nt[i] = 1;
else lt[i] = i - 1, nt[i] = i + 1;
}
int ans = 0;
while (!q.empty() && m) {
while (used[q.top().id]) q.pop();
Node now = q.top(); q.pop();
ans += now.w, --m;
used[lt[now.id]] = used[nt[now.id]] = true;
a[++n] = a[lt[now.id]] + a[nt[now.id]] - now.w;
nt[lt[lt[now.id]]] = n, lt[nt[nt[now.id]]] = n;
lt[n] = lt[lt[now.id]], nt[n] = nt[nt[now.id]];
Node x;
x.w = a[n], x.id = n;
q.push(x);
}
std::cout << ans << '\n';
return 0;
}