其他分享
首页 > 其他分享> > P1484 种树

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;
}

标签:const,P1484,val,int,种树,heap,sb,maxn
来源: https://www.cnblogs.com/Garen-Wang/p/10959564.html