P1484 种树

这是一道不错的dp转反悔贪心题。

\(n\)\(k\)很小的时候,肯定能想到dp。章口就莱。

但是现在\(n \leq 500000\),当场去世。

我们考虑在\(i\)种树,会获得\(a_i\)的获利。

当然,我们也能不在\(i\)种,转而在\(i-1\)\(i+1\)种。会获得\(a_{i-1}+a_{i+1}\)的利润。

可不可以反悔呢?实际上是可以的。

在选完\(i\)后,我们可以把第\(i\)棵数的价值改下,改为\(a_{i-1}+a_{i+1}-a_i\)。选了这件,相当于不种\(i\),转而种它左右两边的树。对答案是完全没有影响的。

所以我们这么做:

维护一个大根堆,节点储存值和对应下标,以值为关键字。

最开始把所有元素都扔进去。之后进行\(k\)次操作。

每次操作,我们拿出里面的堆顶,然后把答案加上。再对应修改,修改为左右的价值减掉自己的价值。

如何记录左右的元素?直接用一个双向链表就完事啦!

代码:

#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
#define ll long long
const int maxn = 500005;
const int INF = 0x3f3f3f3f;
ll a[maxn];
int n, m;
int l[maxn], r[maxn];
ll ans;
bool done[maxn];
struct Nodes {
    ll val, idx;
    bool operator < (const Nodes &rhs) const {
        return val < rhs.val;
    }
};
std::priority_queue<Nodes> heap;
int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        l[i] = i - 1; r[i] = i + 1;
        heap.push((Nodes){a[i], i});
    }
    r[0] = 1; l[n + 1] = n;
    while(m--) {
        while(done[heap.top().idx]) heap.pop();
        Nodes sb = heap.top(); heap.pop();
        if(sb.val < 0) break;
        ans += sb.val;
        int x = sb.idx;
        a[x] = a[l[x]] + a[r[x]] - a[x];
        sb.val = a[x];
        done[l[x]] = done[r[x]] = true;
        l[x] = l[l[x]]; r[l[x]] = x;
        r[x] = r[r[x]]; l[r[x]] = x;
        heap.push(sb);
    }
    cout << ans << endl;
    return 0;
}

转载于:https://www.cnblogs.com/Garen-Wang/p/10959564.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值