[堆] 种树

题目描述

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以内。

题解

注意到题目中的限制条件,如果在一个坑中种了树,那么这个坑两边的坑就不可以再种树

那么就有这种情况出现,对任意一个坑id,其左边的坑l[id]及r[id],选择max(v[id],v[l[id]]+v[r[id]])

正解其实为优先队列(大根堆),首先将所有的坑全部丢入堆中维护,然后最多种满k个坑,所以从1枚举到k,可以不种满,这种情况下肯定是因为种下这棵树的价值是负的,对我们来说没有意义

1~k的循环中每次取出堆顶,直接将其加到ans中(这肯定是不对的,但是之后我们会进行比较,即事实上我们的堆中维护的是v[l[id]]+v[r[id]]-v[id],所以下次取出来后,如果这是一个正数,那么我们实际上加的是v[l[id]]+v[r[id]]),将其与左右两个坑的值得和比较,然后作差丢入堆中,并将这三个点全部打上标记(不管选择哪种方案这三个坑都不能再使用了),然后将这个值赋给在n个坑之后的空位中,即安排一个变量num,初始值为n,并将这个坑num左端点链到l[l[id]],右端点链到r[r[id]](因为l[id],r[id]这两个坑都是不能用的),再维护其左右端点

lol l1=l[x.id],r1=r[x.id];
l[num]=l[l1];r[num]=r[r1];
r[l[num]]=num;l[r[num]]=num;

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define in(i) (i=read())
using namespace std;
typedef long long lol;
lol read()
{
    lol ans=0,f=1;
    char i=getchar();
    while(i<'0'||i>'9') {
        if(i=='-') f=-1;
        i=getchar();
    }
    while(i>='0' && i<='9'){
        ans=(ans<<1)+(ans<<3)+i-'0';
        i=getchar();
    }
    return ans*f;
}
const lol N=500000;
lol n,k;
struct node{
    lol val,id;
}t[N+10];
priority_queue<node>q;
bool operator < (node a,node b){
    return a.val<b.val;
}
lol l[N+10],r[N+10],a[N+10],vis[N+10];
int main()
{
    lol ans=0;
    in(n);in(k);
    for(lol i=1;i<=n;i++){
        in(t[i].val);
        t[i].id=i;
        a[i]=t[i].val;
        l[i]=i-1;r[i]=i+1;
        q.push(t[i]);
    }
    lol num=n;
    for(lol i=1;i<=k;i++){
        node x=q.top();
        q.top();
        while(!q.empty() && vis[x.id]){
            x=q.top();
            q.pop();
        }
        if(x.val<0) break;
        ans+=x.val;num++;
        vis[x.id]=1;
        vis[l[x.id]]=1;
        vis[r[x.id]]=1;
        lol l1=l[x.id],r1=r[x.id];
        l[num]=l[l1];r[num]=r[r1];
        r[l[num]]=num;l[r[num]]=num;
        a[num]=a[l1]+a[r1]-a[x.id];
        q.push((node){a[num],num});
    }
    cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/real-l/p/9188014.html

时间: 2024-10-08 20:45:06

[堆] 种树的相关文章

【bzoj2151】种树(堆/优先队列+双向链表)

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2151 这道题因为优先队列不怎么会用,而且手写堆的代码也不长,也想复习一下手写堆的写法……结果……WAWAWAWAW……看来我对堆的认识还是太浅薄了…… 这道题,如果没有限制不能选相邻的两个位置,那就肯定是贪心地选择m个美观度最大的位置种树.然而这里加了限制,那么我们可以注意到,如果一个美观度比较大的位置不被选上,一定是因为它两边的位置都被选了. 于是我们可以把n个美观度压进一个堆里,每

【洛谷】【堆+贪心】P1484 种树

[题目描述:] cyrcyr今天在种树,他在一条直线上挖了n个坑.这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树.而且由于cyrcyr的树种不够,他至多会种k棵树.假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利. [输入格式:] 第一行,两个正整数n,k. 第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利. [输出格式:] 输出1个数,表示cyrcyr种树的最大获利. 输

种树 (堆模拟网络流)

种树 题目描述 cyrcyr今天在种树,他在一条直线上挖了n个坑.这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树.而且由于cyrcyr的树种不够,他至多会种k棵树.假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利. 输入输出格式 输入格式: 第一行,两个正整数n,k. 第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利. 输出格式: 输出1个数,表示cyrcyr种树的最大获利

P1484 种树(堆)

堆优化的贪心,考虑种一棵树的最大收益,种了当前树两旁的树之后的收益为a[i-1]+a[i+1]-a[i] 用双向链表维护住左右关系,大根堆则可以“反悔”,维护另一个记录某个坑能不能种树的数组即可 代码: #include <bits/stdc++.h> #define int long long #define sc(a) scanf("%lld",&a) #define scc(a,b) scanf("%lld %lld",&a,&am

【BZOJ 2151】 2151: 种树 (贪心+堆)

2151: 种树 Description A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树.园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n.并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度.但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置.值得注意的是1号和n号也算相邻位置!).最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大.如果无法将m棵树苗

bzoj2151: 种树(双向链表+堆)

题目大意:n个数围成一个圈,选了某个数就不能选它相邻的两个数,问选m个数,最大值为多少. 先用堆维护n个数的最大值,每次取出最大值加进答案,选了某个数之后有可能选它相邻的两个比他更优,所以把旁边的两个数删掉,把这次选的数的值改成左边的数+右边的数-这次选的数,如果下次取了这个数相当于不选这个数换成另外两个,选择m次就是取了m个数.一个数的左右两边的数就用双向链表来搞就行了. 代码如下: var n,m,x,i,ans:longint; l,r,heap,pos,a:array[0..200001

bzoj 2151: 种树【贪心+堆】

和数据备份差不多 设二元组(i,a[i]),开一个大根堆把二元组塞进去,以len排序,每次取出一个二元组 因为单纯的贪心是不行的,所以设计一个"反悔"操作. 记录二元组的前驱pr后继ne,把拿出来的二元组的len加进答案,然后把当前二元组和它的前驱后继当成一个,也就是len[x]=a[pr[x]]+a[ne[x]]-a[x],相应修改前驱后继,然后"删掉"前驱后继的二元组,这样的意思是如果再次选了修改过的二元组x,就意味着退还掉x,选择它的前驱后继,易证这样个数和a

P1484 种树 - 堆 - 贪心

这题想得脑阔疼...我只想到可以选一个点,或者不选这个点选其左右两个点的和 先来特殊情况,k=1, 然后k=2 可以发现由1到2的过程中,可以是一个本来被选的点,替换为他左右两边的点,收益增加了a[pos+1] + a[pos-1] - a[pos] 这个题是一个个选,直到选了k个,有种递推的感觉,先确定种了前面几个,再确定这一个该怎么种 然后我不会处理若有别的点也选上,并且影响到这个pos+1和pos-1的情况 事实上可以把这三个点缩在一起啊(当然不能提前缩,要在pos这个点被选时缩起来) 然

种树(贪心)

2151: 种树 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 714  Solved: 397[Submit][Status][Discuss] Description A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树.园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n.并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度.但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i