名校联赛DAY.2A层第二题SO(就)题解

问题 B: 就

时间限制: 1 Sec  内存限制: 512 MB

题目描述

就so.in/.out

【背景描述】

一排 N 个数, 第 i 个数是 Ai , 你要找出 K 个不相邻的数, 使得他们的和最大。

请求出这个最大和。

【输入格式】

第一行两个整数 N 和 K。

接下来一行 N 个整数, 第 i 个整数表示 Ai 。

【输出格式】

一行一个整数表示最大和, 请注意答案可能会超过 int 范围

【样例输入】

3 2

4 5 3

【样例输出】

7

【数据范围】

对于 20% 的数据, N, K ≤ 20 。

对于 40% 的数据, N, K ≤ 1000 。

对于 60% 的数据, N, K ≤ 10000 。

对于 100% 的数据, N, K ≤ 100000 , 1 ≤ Ai ≤ 1000000000。

  这道题第一反应是DP的举起你们的双手,你们尽管打,A了算我输。

  这道题不得不说所有人都以为是DP,但DP再怎么优化也逃不开对N,K的枚举,因此只要是DP基本到n^2就已经是极限了,我就打了n^2,果然,60%,虽然不知道有人也是这个复杂度却过不了的原因,但n^2打的漂亮确乎是60分。因此DP这条路我们不得不放弃。

  那么问题来了,求最优解我们还能打啥呢?DP扑街了,那么只能是贪心了吧,那么怎么贪呢,见一个拿一个很明显是错解,那就让我们回到这个题目对贪心唯一的限制—相邻。

  相邻就意味着我一旦选了这个点,它周围的两个点都不能选了,那么怎么去利用这个性质呢?我们大可去把它和它周围两点连起来看,为什么不能在贪这个数的时候把它和其他数建立联系呢,那么问题来了,什么东西可以在极短的时间内找到它的前后两点(当然不算遍历),链表。

  我们大可用链表将该点与它左右两边还没被选上(我们暂且这么说,但从实际意义上来看它是不对的)的点连接起来,只要这个点被选上,就改变链表结构。

  大家可能看的一头雾水,好吧,我承认,我语文不好。现在开始讲贪心。

  首先我们先用优先队列将每个节点按照权值从大到小排列起来,同时保存每个节点所代表的区间的左右边界。每次从堆顶取元素时确认是否合法,即他的左右端点之一是否被覆盖,如果是就继续pop。

 “代表”“覆盖”是什么意思呢? 每次我们从堆顶选出来一个最大的位置,然后将其与左右两个相邻区间通过链表合并,并将这三个位置打上标记,以便删除,我们个左右两个区间打上标记并不是因为不再去考虑他们了,而是为了方便接下来的操作,我们要对当前点所维护的信息进行修改,首先是权值,权值改为作区间权值+右区间权值-原权值,这样当我们将这个节点再次拿出来的时候就可以去代表我们选择了它两端的权值了,然后将左右端点更新,链表更新即可,由于本题证明虽然容易但难以用语言表述,只能请读者自己证明了。

  

 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<queue>
 8 #include<map>
 9 #include<cmath>
10 using namespace std;
11 int m,n;
12 long long a[100005];
13 map<int,bool> ma[100005];
14 struct no{
15     long long data;
16     int bh;
17     bool friend operator < (no a,no b)
18     {
19         if(a.data==b.data) return a.bh>b.bh;
20         return a.data<b.data;
21     }
22 };
23 priority_queue<no> q1;
24 int pre[100005],fro[100005];
25 int le[100005],ri[100005];
26 int main(){
27     scanf("%d%d",&n,&m);
28     for(int i=1;i<=n;i++)
29     {
30         scanf("%lld",&a[i]);
31         no x;
32         x.data=a[i];
33         x.bh=i;
34         le[i]=i;
35         ri[i]=i;
36         pre[i]=i-1;
37         fro[i]=i+1;
38         q1.push(x);
39     }
40     long long ans=0;
41     fro[n]=0;
42     a[0]=-1e15;
43     while(m--)
44     {
45         while(!q1.empty()&&ma[le[q1.top().bh]][ri[q1.top().bh]])
46             q1.pop();
47         no x=q1.top();
48         q1.pop();
49         ans+=x.data;
50         a[x.bh]=-x.data;
51         x.data=-x.data;
52         x.data+=a[pre[x.bh]],a[x.bh]+=a[pre[x.bh]];
53         x.data+=a[fro[x.bh]],a[x.bh]+=a[fro[x.bh]];
54         ma[le[pre[x.bh]]][ri[pre[x.bh]]]=ma[le[fro[x.bh]]][ri[fro[x.bh]]]=1;
55
56         le[x.bh]=le[pre[x.bh]];
57         ri[x.bh]=ri[fro[x.bh]];
58         if(pre[pre[x.bh]])
59             fro[pre[pre[x.bh]]]=x.bh;
60         if(fro[fro[x.bh]])
61             pre[fro[fro[x.bh]]]=x.bh;
62         pre[x.bh]=pre[pre[x.bh]];
63         fro[x.bh]=fro[fro[x.bh]];
64         q1.push(x);
65     }
66     printf("%lld\n",ans);
67     return 0;
68 }
69  

  其实本题set可以更快,有兴趣的话可以试一试。

时间: 2024-10-22 21:53:33

名校联赛DAY.2A层第二题SO(就)题解的相关文章

名校联赛DAY.2A层第一题passward题解

问题 A: Passward 时间限制: 1 Sec  内存限制: 512 MB 题目描述 你来到了一个庙前,庙牌上有一个仅包含小写字母的字符串 s. 传说打开庙门的密码是这个字符串的一个子串 t,并且 t 既是 s 的前缀又是 s的后缀并且还在 s 的中间位置出现过一次. 如果存在这样的串,请你输出这个串,如有多个满足条件的串,输出最长的那一个. 如果不存在这样的串,输出"Just a legend"(去掉引号). 输入格式: 仅一行,字符串 s. 输出格式: 如题所述 样例输入 f

名校联赛DAY.2A层第三题book(书)题解

书 时间限制: 1 Sec  内存限制: 512 MB 题目描述 书 book.in/.out Hazel有n本书,编号1为n到 ,叠成一堆.当她每次抽出一本书的时候,上方的书会因重力而下落,这本被取出的书则会被放置在书堆顶. 每次有pi的概率抽取编号为i的书.她每次抽书所消耗的体力与这本书在这堆中是第几本成正比.具体地,抽取堆顶的书所耗费体力值为1 ,抽取第二本耗费体力值为2 ,以此类推. 现在 想知道,在很久很久以后(可以认为几乎是无穷的),她每次抽书所耗费的体力的期望值是多少. 最终的答案

liu_runda 给辣鸡蒟蒻做的 NOIP模拟赛 1.0 第二题 任(duty) 题解

问题 B: 任(duty) 时间限制: 2 Sec  内存限制: 512 MB 题目描述 liu_runda退役之后就失去梦想开始咸鱼生活了- Bilibili夏日画板活动中,所有人都可以在一块画板上进行像素画创作.UOJ群有一群无聊的人决定在画板上创作一个50*50的UOJ的LOGO.如下图. 这块画板实际上是很大的矩形网格.一个网格是一像素. 一个人每三分钟才能画一个像素.所以liu_runda的咸鱼生活非常无聊. 郭神表示他实在是看不下去liu_rudna这只颓狗了,于是随手出了一道神题,

洛谷2018寒假集训tg第二次比赛第二题Princess Principal题解

这算不算泄题啊...被kkk发现会咕咕咕吧. 题目大意:给定一个数列a,与常数n,m,k然后有m个询问,每个询问给定l,r.问在a[l]到a[r]中最少分成几段,使每段的和不超过k,如果无解,输出Chtholly 样例: input: 5 5 72 3 2 3 43 34 45 51 52 4 output: 11122 解答: 首先观察数据范围,n<=1e+6 可能的复杂度为O(mlogn).暴力能搞30分吧. 其实这题还是很妙的.我们先考虑暴力:对于[L,R],设个res,对[l,r]从左往

csp 2017年12月第二题 游戏 Python题解100分

题意就不重复了,其实就是num代表这些人的一个列表,先全部为1,如果某个编号的人淘汰了,使该编号的人为0,内循环i从1到n,如果num[i]不为0,计数++,然后判断..... n,k=map(int,input().split(" ")) num=[1 for i in range(1,n+1)] num2=[i for i in range(1,n+1)] num.insert(0,0) ans=n x=0 t=0 if(k==1): print(n) else: while(an

“金山杯2007逆向分析挑战赛”第一阶段第二题

注:题目来自于以下链接地址: http://www.pediy.com/kssd/ 目录:第13篇 论坛活动 \ 金山杯2007逆向分析挑战赛 \ 第一阶段 \ 第二题 \ 题目 \ [第一阶段 第二题] 题目描述: 己知是一个 PE 格式 EXE 文件,其三个(section)区块的数据文件依次如下:(详见附件)  _text,_rdata,_data 1. 将 _text, _rdata, _data合并成一个 EXE 文件,重建一个 PE 头,一些关键参数,如 EntryPoint,Imp

经典算法题每日演练——第二题 五家共井

原文:经典算法题每日演练--第二题 五家共井 古代数学巨著<九章算数>中有这么一道题叫“五家共井,甲二绠(汲水用的井绳)不足,如(接上)乙一绠:乙三绠不足,如丙一绠: 丙四绠不足,如丁一绠:丁五绠不足,如戊一绠:戊六绠不足,如甲一绠,皆及. 意思就是说五家人共用一口井,甲家的绳子用两条不够,还要再用乙家的绳子一条才能打到井水:乙家的绳子用三条不够,还要再用丙家的绳子 一条才能打到井水:丙家的绳子用四条不够,还要再用丁家的绳子一条才能打到井水:丁家的绳子用五条不够,还要再用戊家的绳子一条才能打

第二题系列

第二题一般应该是多层for循环吧....... 2017-12-2游戏循环报数: 问题描述 有n个小朋友围成一圈玩游戏,小朋友从1至n编号,2号小朋友坐在1号小朋友的顺时针方向,3号小朋友坐在2号小朋友的顺时针方向,……,1号小朋友坐在n号小朋友的顺时针方向. 游戏开始,从1号小朋友开始顺时针报数,接下来每个小朋友的报数是上一个小朋友报的数加1.若一个小朋友报的数为k的倍数或其末位数(即数的个位)为k,则该小朋友被淘汰出局,不再参加以后的报数.当游戏中只剩下一个小朋友时,该小朋友获胜. 例如,当

2016/1/12 第一题 输出 i 出现次数 第二题 用for循环和if条件句去除字符串中空格 第三题不用endwith 实现尾端字符查询

1 import java.util.Scanner; 2 3 4 public class Number { 5 6 private static Object i; 7 8 /* 9 *第一题 mingrikejijavabu中字符“i” 出现了几次,并将结果输出*/ 10 public static void main(String[] args) { 11 12 String r ="imingrikejijavabi"; 13 14 15 //第一种 截取 16 int a=