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

题目大意:n个数围成一个圈,选了某个数就不能选它相邻的两个数,问选m个数,最大值为多少。

先用堆维护n个数的最大值,每次取出最大值加进答案,选了某个数之后有可能选它相邻的两个比他更优,所以把旁边的两个数删掉,把这次选的数的值改成左边的数+右边的数-这次选的数,如果下次取了这个数相当于不选这个数换成另外两个,选择m次就是取了m个数。一个数的左右两边的数就用双向链表来搞就行了。

代码如下:

var
  n,m,x,i,ans:longint;
  l,r,heap,pos,a:array[0..200001]of longint;

procedure down(i:longint);
var
  t,j:longint;
begin
  while (i<<1<=n) do
  begin
    if (i<<1=n)or(a[heap[i<<1]]>a[heap[i<<1 or 1]]) then j:=i<<1
    else j:=i<<1 or 1;
    if a[heap[i]]<a[heap[j]] then
    begin
      t:=heap[i];heap[i]:=heap[j];heap[j]:=t;
      t:=pos[heap[i]];pos[heap[i]]:=pos[heap[j]];pos[heap[j]]:=t;
      i:=j;
    end
    else exit;
  end;
end;

procedure up(i:longint);
var
  t:longint;
begin
  while i>1 do
  begin
    if a[heap[i]]>a[heap[i>>1]] then
    begin
      t:=heap[i];heap[i]:=heap[i>>1];heap[i>>1]:=t;
      t:=pos[heap[i]];pos[heap[i]]:=pos[heap[i>>1]];pos[heap[i>>1]]:=t;
      i:=i>>1;
    end
    else exit;
  end;
end;

begin
  readln(n,m);
  if (m>n>>1) then writeln(‘Error!‘)
  else
  begin
    for i:=1 to n do
    begin
      read(a[i]);
      l[i]:=i-1;r[i]:=i+1;heap[i]:=i;pos[i]:=i;up(i);
    end;
    l[1]:=n;r[n]:=1;
    for i:=1 to m do
    begin
      x:=heap[1];inc(ans,a[x]);
      a[x]:=a[l[x]]+a[r[x]]-a[x];down(pos[x]);
      a[l[x]]:=-maxlongint;down(pos[l[x]]);l[x]:=l[l[x]];
      a[r[x]]:=-maxlongint;down(pos[r[x]]);r[x]:=r[r[x]];
      l[r[x]]:=x;r[l[x]]:=x;
    end;
    writeln(ans);
  end;
end.

PS:我才知道堆可以这么拿来删元素QAQ

时间: 2024-08-02 15:52:03

bzoj2151: 种树(双向链表+堆)的相关文章

bzoj 1150&amp;2151&amp;2288(双向链表+堆)(贪心)

经典模型:在n个点中选k个点,要求两两不相邻,且总权值最大/最小. 做法:用双向链表串起来,把所有点丢进堆里,选择一个点的时候把它左右两个点从双向链表和堆中去除,然后把这个点的权值加进ans,出堆后改为左右两边的权值-当前权值重新入堆,重复k次,ans即为答案 原理:左右两边的权值-当前权值相当于这个点不选,改为选左右两边的点,并且多选了一个点,重复k次后必然取到k个点 三道同类型题 bzoj1150:显然一段网线肯定接在相邻的两个点,于是把相邻的点的距离求出来,问题就变成经典模型了 #incl

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

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

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

BZOJ2151 种树

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

bzoj3502[PA2012]Tanie Linie(最大k区间和)

题意:给定一个长为n的数列,要求选出最多k个不相交的区间(可以不选),使得选中的数字之和最大.(1<=k<=n<=1000000)分析:首先我们通过预处理对问题做一些简化.原序列中的0对答案没有影响,可以直接删掉.连续的一段正数或一段负数一定是都选或者都不选,可以合并成一个数字.这样把序列转化成了正数和负数交替出现的形式.如果序列的最左端/最右端是负数,这个负数在最优解当中一定不会被选中,我们可以把它删掉.这样就把序列变成了正负交替,以正数开头和结尾的形式.这时若直接考虑选出k个不相交区

反悔贪心

目录: 个人理解 反悔贪心的分类 反悔自动机 反悔堆 例题简析及代码 一.个人理解: 贪心本身是没有反悔操作的,贪心求的就是当前的最优解.但当前的最优解有可能是局部最优解,而不是全局最优解,这时候就要进行反悔操作. 反悔操作指的是这一步的贪心不是全局最优解,我们就退回去一步(人工或自动判断),换一种贪心策略.按照判断方式的不同可以分为反悔自动机和反悔堆两种方法. 二.反悔贪心的分类: 反悔自动机: 即设计一种反悔策略,使得随便一种贪心策略都可以得到正解. 基本的设计思路是:每次选择直观上最接近全

二模 (13)day2

第一题: 题目大意: 给出一个N*M的矩阵,定义一条路径的权值为经过的所有点权值的最大值.求一条从第一行到第N行的路径,使得路径权值最小. N,M<=1000 矩阵内点的权值小于1000. 解题过程: 1.感觉有点像vijos的晴天小猪,有后向性的dp,当时用了spfa来做,于是这题也用spfa了.听说网格图可以卡掉spfa,以前一直不相信,结果今天就被网格图卡死了. TLE 5个点. 50分 2.AC算法:考虑点的权值小于1000,可以直接二分答案&&flood—fill 即可.

bzoj1124[POI2008]枪战maf tarjan+树规+贪心/线性DP

这代码快写死我了.....死人最多随便推推结论.死人最少,每个环可以单独考虑,每个环上挂着的每棵树也可以分别考虑.tarjan找出所有环,对环上每个点,求出选它和不选它时以它为根的树的最大独立集(就是最多活下来的人数),然后环上每个点选或不选对应的是一个“价值”,这个价值是那个点挂着的树里最多存活人数.先全都不选环上的点,算出选和不选时最大独立集的差值,问题变成有一个环,环上有一堆数(那些差值),选出一些不相邻的数使得和最大,然后我按着bzoj2151种树写了个贪心....这个贪心的思路也很神,

WQS二分题集

WQS二分,一种优化一类特殊DP的方法. 很多最优化问题都是形如“一堆物品,取与不取之间有限制.现在规定只取k个,最大/小化总收益”. 这类问题最自然的想法是:设f[i][j]表示前i个取j个的最大收益,转移即可.复杂度O(n^2). 那么,如果在某些情况下,可以通过将问题稍作转化,变成一个不强制选k个的DP,而最后DP出来的最优解一定正好选了k个,那么问题就会简化很多. WQS二分就是基于这个思想. 首先考虑建一个二维坐标系,x轴是选的数的个数,y轴是最大收益,如果这个x-y图像有凸性,那么就