BZOJ 2151: 种树

题目链接:传送门
在一个 n n n个元素的环中选取 m m m个元素,要求全部选上,且选中一个元素后两边的元素不可选,求最大价值,如不能选出 m m m个输出 − 1 -1 1

很简单也很难的样子~~~
选出一个元素后两边的元素就不能选了
所以一个位置的决策可能会影响许多位置
如果我们贪心的选取最大值
那么可能两边的元素的和比这个元素大
所以这样的贪心是肯定不对的
那么我们需要一个可能让两边的元素被选上的机会
也就是可以反悔选上两边的元素
怎么搞呢
取出一个位置 i i i的元素后
我们新加入一个元素,价值为 v a l [ p r e [ i ] ] + v a l [ n e x t [ i ] ] − v a l [ i ] val[pre[i]]+val[next[i]]-val[i] val[pre[i]]+val[next[i]]val[i]
就是两边元素的价值减去这个元素的价值
那么我们在优先队列里选最大值的时候
如果选中我们新加入的这个元素
那么就相当于没选位置 i i i的元素而是选了 i i i两边的元素
因为 v a l [ i ] val[i] val[i]被抵消了,因为我们之前加了一个 v a l [ i ] val[i] val[i]
所以我们需要维护每个元素的前驱后继,也就是上面的 p r e pre pre n e x t next next
还要注意边界问题,因为这是个环
挺明白的了吧~

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <algorithm>
#include <climits>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define A 1000010
#define B 2010

using namespace std;
typedef long long ll;
struct node {
    int id, val;
    bool operator < (const node b) const {
        return val < b.val;
    }
};
priority_queue<node> q;
ll n, k, val[A], nxt[A], pre[A], can[A], cf[A], ans;

int main(int argc, char const *argv[]) {
    cin >> n >> k;
    if (k * 2 > n) return puts("Error!"), 0;
    for (int i = 1; i <= n; i++) cin >> val[i];
    for (int i = 1; i <= n; i++) pre[i] = i - 1, nxt[i] = i + 1;
    pre[1] = n; nxt[n] = 1;
    for (int i = 1; i <= n; i++) q.push(node{i, val[i]});
    for (int i = 1; i <= k; i++) {
        while (can[q.top().id]) q.pop();
        node x = q.top(); q.pop();
        ans += x.val;
        ll a = pre[x.id], b = nxt[x.id], va = val[a] + val[b] - val[x.id];
        val[x.id] = va;
        q.push(node{x.id, va});
        pre[x.id] = pre[a], nxt[pre[a]] = x.id, can[a] = 1;
        nxt[x.id] = nxt[b], pre[nxt[b]] = x.id, can[b] = 1;
    }
    cout << ans << endl;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良月澪二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值