题解 P1484 种树

题目

传送门

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

思路

开始没有想到,这是一道可以反悔的贪心题。

我们可以用大根堆来维护最大获利的树,用链表来维护每棵树左右的树。

每次选择了最大的树,我们要把此树左右两边 vis 标记为1,下次枚举到vis[x] = 1continue

但有可能选左右两颗树比选中间这颗树情况更优,于是我们要在加入一颗树,值为 左树权值+右树权值-中间树权值。

这样,在下一次枚举时,当我们选了这颗树,则答案增加了 左树权值+右树权值-中间树权值+中间树权值 = 左树权值+右树权值。 正好选择了2颗树。

注意:不一定要种满 k 颗树

代码

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>

#define ll long long 
#define mes(x) memset(x,0,sizeof(x))
#define re(x) read(x)
#define il inline

using namespace std;

template <typename T>
il void read(T &x){
	x = 0; T sgn = 1; char ch = getchar();
	for(;!isdigit(ch);ch = getchar()) if(ch=='-') sgn = -1;
	for(;isdigit(ch);ch = getchar()) x = (x<<1)+(x<<3)+(ch^48);
	x *= sgn;
}

template <typename T>
il void write(T x){
	if(x < 0) x = -x,putchar('-');
	if(x == 0) putchar(48);
	int cnt = 0, a[70];
	while(x > 0){
		a[++cnt] = x%10;
		x /= 10;
	}
	for(int i = cnt;i > 0;i--){
		putchar(a[i]+48);
	}
	putchar('\n');
}

const int MAXN = 5e5+10;

int n,k,a[MAXN];
bool vis[MAXN];
ll ans = 0,tot = 0;

struct tree{
	int pos;
	ll val;
	bool operator < (const tree &b) const {
		return val < b.val;
	}
}tr[MAXN];

struct place{
	int l,r;
	ll val;
}p[MAXN];

priority_queue <tree> q;

il void Del(int x){
	p[x].l = p[p[x].l].l;
	p[x].r = p[p[x].r].r;
	p[p[x].l].r = x;
	p[p[x].r].l = x;
}
int main (){
	re(n);re(k);
	for(int i = 1;i <= n;i++){
		re(a[i]);
		p[i].l = i-1;
		p[i].r = i+1;
		p[i].val = a[i];
		q.push((tree){i,a[i]});
	}
    for(int i = 0;i < k;i++){
    	int x = q.top().pos; q.pop();
    	if(vis[x]) {
    		i--;
    		continue;
    	}
    	vis[p[x].l] = vis[p[x].r] =1;
		tot += p[x].val;
		ans = max(tot,ans);
		p[x].val = p[p[x].l].val + p[p[x].r].val - p[x].val;
		q.push((tree){x,p[x].val});
		Del(x);
	}
	write(ans);
	return 0;
} 
posted @ 2020-07-15 08:39  Werner_Yin  阅读(131)  评论(0编辑  收藏  举报