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