「题解」BZOJ 2151.种树

题目

#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;
}
posted @ 2020-08-30 15:48  yu__xuan  阅读(127)  评论(0编辑  收藏  举报