题目描述

马上就要开学了!!!

为了给回家的童鞋们接风洗尘,HZOI帝国的老大决定举办一场狂欢舞会。

然而HZOI帝国头顶上的HZ大帝国十分小气,并不愿意给同学们腾出太多的地方。所以留给同学们开party的地方只有一个教室。

这个教室里有一个长条形的舞池,这个舞池最多能让n个人同时在上面high,也就是说有n个位置,但要知道HZ大帝国对同学们间的接触限制很严(众所周知一群糙老爷们儿也是能够非正常接触的),所以实际上两个人是不可以在相邻的位置上high的。

由于不同的位置high起来的感觉不是很一样,所以每个位置都有一个high值。

但是有强迫症的老大对这个舞池设计不是很满意,于是他下令把条形的舞池改造成了圆环状,也就是说第1个位置和第n个位置现在相邻了。

所以,你的任务是计算合法安排好所有同学所能达到的最大high值和。

输入格式

输入包括两行:

第一行有两个正整数:n,m,代表舞池有n个位置,有m个童鞋要参加狂欢

第二行有n个整数:high[i]即第i个整数代表舞池第i个位置的high值

输出格式

输出共一行:

一个整数即安排好所有同学能达到的最大high值和

如果无法安排好所有的同学,则输出“High!!!”(不含引号)。

输入输出样例

输入

5 2
1 2 3 4 5

输出

8

输入

8 3
2 7 14 8 -3 0 4 9

输出

24

说明/提示

1 \(\leq\) m \(\leq\) n \(\leq\) 200000

| high[i] | \(\leq\) 2000

一点题外话

这题的根源是这儿:https://www.luogu.com.cn/problem/P1484

当时我看到这个题就寻思着要整个环,思路基本上是不变的,也算是整了个活,然后就有了这道题。然鹅后来突然发现它下面就有一道名字一毛一样的题,对没错,就是顶着国家集训队名头的那个种树……不过那题的数据范围有点尴尬,那题的很多标程的无解是直接用n<2m判过去的,数据也的确能过,但n=m=1时实际上它是有解的。这一点我开始时就被标程给带跑了,后来还是LC大锅hack了我一波(笑)。

一开始只有一个很普通的样例可能迷惑性很强,所以到一半时我又加了一个样例2,应该是能卡掉不少代码的,也不知道有人看见了没……

整个好活

题意我觉得我的语文应(shen)该(me)还(dou)可(bu)以(shi),就不给大家复述了。

好吧还是简单说一下:就是在一个有n个点的环上取m个点,使这m个点两两不相邻,且取到的权值和最大。

可能有神犇想到用DP,但是200000的数据并不是那么友好。

这题的思路其实就是贪心,但要用到一个很巧妙很巧妙的技巧

首先最简单的贪心估计大家都能想到,那就是建一个大根堆,一个个取,每取一个把两旁相连的点都标记上不能再取。

这肯定是最原始的贪心,无论什么操作都是在这个思路之上进行的。

那么,这个思路到底哪里有毛病呢?

我们简单地模拟一下:7 14 8 0 这4个数中选两个

照我们刚才的思路,肯定第一步先取14,然后7跟8就标记上不能取了

显然下一步我们能取的只有0,这样算出来最优值是14

但是很显然,如果我们直接取7跟8,和是15,显然比咱们现在的14要大。

问题就出在:在点数允许的情况下,我们没有判断当前取出的这个14跟它左右相连的7跟8的和的关系,也就是说我们不能保证全局最优。

可以选择判断a[i-1].w + a[i+1].w - a[i].w与0的大小,如果大于0,我们要这两个点而不要最大的那个点。

但真正去运算时肯定不允许你这样做,我们一次这个运算只能从三个数中做一个选择,那么放到整个程序里就是从n个数中取3个数的组合数,这还是没算别的常数的说。时间显然是不允许的。

所以就要用到下面这个肥常肥常神奇的技巧:

struct node{
int w,l,r;
}a[maxn];//存储每个节点的信息

我们简单开一个结构体,w代表当前节点的权值,l为左节点编号,r为右节点编号。

还是刚才的思路,建大根堆,每次取最大权值点,标记左右两点,但不一样的地方是:我们取完这个点后,再往根堆里插一个新的点,这个新点的权值为原来左右节点的权值和-原来该节点权值,左节点为原左节点的左节点,右节点为原右节点的右节点。

这样实际上有什么用呢?回到我们出错的原因:因为我们每次只能保证在当前情况下取最大那个数是最优的,这是贪心的基础,但我们不能保证这个选择在全局上也是最优的。所以我们需要一个保险,这个新点就是为我们提供一个后悔的选项。

我们记新点的权为 a[i-1].w + a[i+1].w - a[i].w

在当前这一步,我们选择了a[i].w,因为它最大,然后插入新点。那么,如果我们能在取到这个新点之前结束运算,那么显然选择最大的这个数就是最优选择。但如果我们一直取下去直至取到了这个新点,那么说明在刚刚的那一步中a[i].w并不是最优选择,所以我们取到了这个新点,ans就相当于在之前加上了a[i].w,然后现在又加上了a[i-1].w + a[i+1].w - a[i].w

前后合起来刚好是a[i-1].w + a[i+1].w,对于整个答案来说就相当于我们刚才那步选择了左右节点,而没有选择最大节点。

在学校里又重新听老师讲了这道题,发现有可能一个疑问就是新节点到底算几棵树的问题。有的童鞋认为算两棵,因为它不是选择了左右两棵树嘛,所以种树棵数应该++才对。但要知道我们在前面已经在最大的那个坑种树了,如果现在算两棵树,那前面那棵岂不是多了吗。所以这个新结点我们可以理解为把原来的最大坑的一棵树挪到了一边,然后在另一边种一棵新的。

综上所述,在新节点种树时并不用对棵数单独处理,直接按正常的树处理即可。

(ps:这才多长时间我就不忍看自己的代码了...码风好丑...)

#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#include <iostream>
#define lson(x) a[x].l
#define rson(x) a[x].r
using namespace std;
const int maxn=200000+10;
int n,ans,m;
bool vis[maxn];
struct node{
int w,l,r;
}a[maxn];//存储每个节点的信息
struct Node{
int val,id;
Node();
Node(int x,int y){
val=x,id=y;
}
bool operator <(const Node x)const{
return val<x.val;
}
}; priority_queue<Node> q;//大根堆 //为了方便这里直接用左右儿子来解释,实际上是左右相连的点 void Update(int x){//删去一个节点x后更新与x相关的节点
lson(x)=a[lson(x)].l;//新点的左儿子变为原来左儿子的左儿子
rson(x)=a[rson(x)].r;//新点的右儿子变为原来右儿子的右儿子
a[lson(x)].r=x;
a[rson(x)].l=x;
}
int main(){
scanf("%d %d",&n,&m);
int maxx=-1<<15;//可不要设成-1哦,最小值有-2000呢
for(int i=1;i<=n;i++){
scanf("%d",&a[i].w);
maxx=max(a[i].w,maxx);
a[i].l=i-1;
a[i].r=i+1;
q.push(Node(a[i].w,i));
}
if(m==1){//特判掉n=m=1的情况
printf("%d\n",maxx);
return 0;
}
if(n<(m<<1)){//别的情况就可以直接n<2m判掉了
printf("High!!!\n");
return 0;
}
a[1].l=n,a[n].r=1;//所谓的环就只有这一步
for(int i=1;i<=m;i++){
while(1){
if(vis[q.top().id]) {q.pop();continue;}//标记过的点直接pop掉
break;
}
int val=q.top().val,num=q.top().id;
q.pop();
ans+=val;
vis[lson(num)]=vis[rson(num)]=1;//左右相连的点标记为访问过
a[num].w=a[lson(num)].w+a[rson(num)].w-a[num].w;//插入一个新点,新权值为原左右儿子的权值和减去原权值
Update(num);//更新新点的左右儿子信息
q.push(Node(a[num].w,num));
}
printf("%d\n",ans);
return 0;
}

帝国の狂欢(种树)(可撤销DP)的更多相关文章

  1. 【计数DP】种树

    种树 题目描述 事实上,小X邀请两位奆老来的目的远不止是玩斗地主,主要是为了抓来苦力,替他的后花园种树……小X的后花园是环形的,他想在花园周围均匀地种上n棵树,但是奆老花园的土壤当然非同寻常,每个位置 ...

  2. power oj 1557种树[二进制状压DP]

    题目链接[https://www.oj.swust.edu.cn/problem/show/1557] 题意:中文题目. 题解:用0,1表示某个位置是否种了树,先算出同一行的有效状态的总数,即开两个1 ...

  3. DP 优化方法大杂烩 & 做题记录 I.

    标 * 的是推荐阅读的部分 / 做的题目. 1. 动态 DP(DDP)算法简介 动态动态规划. 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 ...

  4. 【海岛帝国系列赛】No.2 海岛帝国:“落汤鸡”市的黑帮危机

    50200210海岛帝国:“落汤鸡”市的黑帮危机 [试题描述] 近几天,犯罪分子发现“药师傅”帝国的警力约等于0.(请见YSF的海岛帝国)于是开始猖狂了起来.他们选择了依山靠水(农村?)的“落汤鸡”市 ...

  5. 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树

    另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...

  6. [swustoj 183] 种树

    种树(0183) 问题描述 Aconly有一块矩形的地,因为这块地里有很多石头,耕作很不方便,所以他打算在这块地上种一些果树.这块地用一个只含‘#’和‘*’的N*M的矩阵来表示,‘#’表示泥土,‘*’ ...

  7. 仿知乎安卓client滑动删除撤销ListView

    标签(空格分隔): Android 新版的知乎安卓client有一个有趣的功能,就是在一个item里.向右滑动时整个item会越来越透明,滑动到一半时,整个item就不见了.放开手指就是删除.删除后还 ...

  8. View实现涂鸦、撤销以及重做功能

    import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import j ...

  9. poj 3254 Corn Fields 国家压缩dp

    意甲冠军: 要在m行n陆行,有一些格您可以种树,别人做不到的.不相邻的树,我问了一些不同的共同拥有的法律. 分析: 从后往前种,子问题向父问题扩展,当种到某一格时仅仅有他和他后面的n-1个格子的情况对 ...

随机推荐

  1. 美女面试官问我Python如何优雅的创建临时文件,我的回答....

    [摘要] 本故事纯属虚构,如有巧合,他们故事里的美女面试官也肯定没有我的美,请自行脑补... 小P像多数Python自学者一样,苦心钻研小半年,一朝出师投简历. 这不,一家招聘初级Python开发工程 ...

  2. 除了FastJson,你也应该了解一下Jackson(一)

    在上月末的时候收到一条关于fastjson安全漏洞的消息,突然想到先前好像已经有好多次这样的事件了(在fastjson上面).关于安全方面,虽然中枪的机率微小,但是在这个信息越来越复杂的时代,安全性也 ...

  3. java之单点登录(SSO)

    单点登录(SSO):SSO是指在多个应用系统中个,用户只需要登陆一次就可以访问所有相互信任的应用系统.它包括可以将这次主要的登录映射到其他应用中用于同一用户的登陆的机制. SSO的实现过程: 通过上述 ...

  4. react使用Echarts绘制高亮可点击选中的省市地图

    最近做项目遇到一个需求,需要显示广东省各个地级市的地图,并且鼠标移入高亮显示,鼠标点击可以选中某个地级市.在网上查阅了大量资料之后,最后选择了使用echarts实现该需求.在此记录一下,希望可以帮到有 ...

  5. @Autowired 注解详解

    前言 我们平时使用 Spring 时,想要 依赖注入 时使用最多的是 @Autowired 注解了,本文主要讲解 Spring 是如何处理该注解并实现 依赖注入 的功能的. 正文 首先我们看一个测试用 ...

  6. 解析D-Bus服务器的地址

    D-Bus 1.13.14 Main Page Related Pages Modules Data Structures Files Typedefs | Functions Address par ...

  7. 【php】 jsonp转数组函数jsonp_decode

    分享一个可以跟json一样用的函数jsonp_decode,能把jsonp格式数据转为php数组或对象. /**  * 把jsonp转为php数组  * @param string $jsonp js ...

  8. 【实战】基于OpenCV的水表字符识别(OCR)

    目录 1. USB摄像头取图 2. 图像预处理:获取屏幕ROI 2.1. 分离提取屏幕区域 2.2. 计算屏幕区域的旋转角度 2.3. 裁剪屏幕区域 2.4. 旋转图像至正向视角 2.5. 提取文字图 ...

  9. @bzoj - 3711@ [PA2014]Druzyny

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若 ...

  10. JavaSE的基本语法

    JavaSE基本语法 一.语法的注意事项 1.严格区分大小写 2.每句命令结尾使用分号 3.符号都是英文状态 4.括号.引号都是成对出现的! 5.注意缩进 Tips: 文件名和类名可以不一致,但pub ...