[LuoguP1484] 种树

题目描述

cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。

输入格式

第一行,两个正整数n,k。

第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。

输出格式

输出1个数,表示cyrcyr种树的最大获利。

输入输出样例

输入 #1
6 3 
100 1 -1 100 1 -1
输出 #1
200

说明/提示

对于20%的数据,n<=20。

对于50%的数据,n<=6000。

对于100%的数据,n<=500000,k<=n/2,在一个地方种树获利的绝对值在1000000以内。

在选了一棵树$x$之后,把相邻的两棵$l$,$r$合并到这棵树上,权值变为$v[l] + v[r] - v[x]$

用大根堆维护权值

用双向链表维护序列

#include <bits/stdc++.h>
using namespace std;
const int maxn = 500000 + 10;
class A{
public:
    int id;
    long long val;
    A(){}
    A(int id, long long val): id(id), val(val){}
    friend bool operator < (const A &x, const A &y){
        return x.val < y.val;
    }
}t;
priority_queue<A> q;
long long num[maxn];
int L[maxn], R[maxn];
bool del[maxn] = {};
int main(){
    int n, k;
    cin >> n >> k;
    for(int i = 1; i <= n; i++){
        cin >> num[i];
        q.push(A(i, num[i]));
        L[i] = i - 1;
        R[i] = i + 1;
    }
    num[0] = num[n + 1] = -(1LL << 60);
    long long ans = 0;
    while(k){
        t = q.top(); q.pop();
        if(t.val <= 0) break;
        if(del[t.id]) continue;
        k--;
        ans += num[t.id];
        num[t.id] = num[L[t.id]] + num[R[t.id]] - num[t.id];
        del[L[t.id]] = del[R[t.id]] = true;
        L[t.id] = L[L[t.id]];
        R[t.id] = R[R[t.id]];
        R[L[t.id]] = t.id;
        L[R[t.id]] = t.id;
        q.push(A(t.id, num[t.id]));
    }
    cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/ruoruoruo/p/12101416.html