2019/11/14 更新日志:

近期发现这篇题解有点烂,更新一下,删繁就简,详细重点。代码多加了注释。就酱紫啦!


正解步骤

  1. 我们需要先算美妙度的前缀和,并初始化RMQ。

  2. 循环 \(i\) 从 \(1\) 到 \(n\) ,因为以i为起点的 和弦 终点必定是 \(i + L - 1\) 到 \(i + R - 1\) 之间,所以只要在区间内用RMQ取 超级和弦 ,并加入以美妙度从小排到大的优先队列中。

  3. 取出堆顶元素,将美妙度加入 \(ans\) ,并将元素切为从 (当前元素的左边界 到 当前元素终点 - 1) 和 (\(当前元素终点 + 1 到 当前元素右边界\)) 两个部分,并再次加入优先队列,依次进行 \(k\) 次。

  4. 输出答案即可


为什么要使用前缀和 and RMQ?

数据范围是500000,很明显,为了优化需要前缀和 and RMQ。

前缀和最明显的用处,是可以优化一个用来循环累加和的 \(n\) 。而 \(RMQ\) ,显然区间最值符合题目要求。

这两个算法询问答案都是O(1)。前缀和后面减去前面,RMQ只需要初始化一下,然后O(1)询问即可。


为什么第三步要切开元素并放入优先队列? 直接累加前 \(k\) 个元素不行么?

首先,我们肯定可以确定:优先队列中第一大的和弦一定是 \(全局\) 最大的和弦。 不要问我怎么证明

那么优先队列中第二大的和弦一定是 \(全局\) 次大的和弦么?这就不一定了。

所以我们需要切开元素并放入优先队列,保证每次取出来的元素一定是全局大小排名的元素

自己拿出纸和笔,结合题解自己思考,在草稿纸上演算一下,就懂了


那我就把解题思路放上吧233

实在还有问题,私信本人233

感谢老师 @apple365 的思路指引。

AC代码

#include<bits/stdc++.h>
#include<cctype>
#pragma GCC optimize(2) #define in(a) a = read()
#define out(a) write(a)
#define outn(a) out(a),putchar('\n') #define ll long long
#define Min(a,b) a < b ? a : b
#define Max(a,b) a > b ? a : b
#define rg register
#define New ll using namespace std; namespace IO_Optimization{ inline New read()
{
New X = 0,w = 0;
char ch = 0; while(!isdigit(ch))
{
w |= ch == '-';
ch=getchar();
}
while(isdigit(ch))
{
X = (X << 3) + (X << 1) + (ch ^ 48);
ch = getchar();
}
return w ? -X : X;
} inline void write(New x)
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) write(x/10);
putchar(x % 10 + '0');
} #undef New
}
using namespace IO_Optimization;//上面一坨优化的东西不用在意 const int MAXN = 500000 + 2;//定义常亮 int n,k,L,R;
int sum[MAXN],lg[MAXN],dp[MAXN][20],pos[MAXN][20];
// 前缀和 lg2值
//dp[i][j]表示i的2^j次方祖先 pos数组来记录最佳位置
ll ans;
struct Node
{
int start, left, right, t, val;
//超级和弦的起点 左、右边界 最值位置 最值
bool operator < (const Node &next) const
{
return val < next.val; //最值从大到小排序
}
}; inline void RMQ_init() //预处理
{
for(rg int j = 1;j <= 20; ++j)
for(rg int i = 1;i + (1 << j) - 1 <= n; ++i)
{
if(dp[i][j - 1] > dp[i + (1 << (j - 1))][j - 1])
{
dp[i][j] = dp[i][j - 1];
pos[i][j] = pos[i][j - 1];//更新最值位置
}
else
{
dp[i][j] = dp[i + (1 << (j - 1))][j - 1];
pos[i][j] = pos[i + (1 << (j - 1))][j - 1];
}
}
return;
} inline int RMQ_query(int l, int r) //返回最值的位置
{
int t, tmp = lg[r - l + 1];
if(dp[l][tmp] > dp[r - (1 << tmp) + 1][tmp])
t = pos[l][tmp];
else t = pos[r - (1 << tmp) + 1][tmp];
return t;
} int main()
{
in(n),in(k),in(L),in(R);
lg[0] = -1;//lg2(0) = -1,方便后面预处理lg2值
for(rg int i = 1;i <= n; ++i)
{
int a = read();//读入音符
sum[i] = sum[i - 1] + a; //前缀和
lg[i] = lg[i >> 1] + 1; //预处理lg2值
dp[i][0] = sum[i];
pos[i][0] = i; //初始化最大值的位置
}
RMQ_init();//初始化
priority_queue<Node> pq; //定义优先队列
for(rg int i = 1;i + L - 1 <= n; ++i) //计算每个位置最大的超级和弦
{
int t = RMQ_query(i + L - 1, Min(n, i + R - 1));
Node cur;
cur.val = sum[t] - sum[i - 1]; //由前缀和取最大值
cur.t = t;
cur.start = i; //当前超级和弦的起始位置
cur.left = i + L - 1; //当前的左边界
cur.right = Min(n,i + R - 1); //当前的右边界
pq.push(cur); //入堆
} for(rg int i = 1;i <= k; ++i) //取k次堆顶的值
{
Node cur = pq.top();
pq.pop();
ans = ans + cur.val; //累加结果
Node next; if(cur.t > cur.left) //当前取最值的位置 大于 当前和弦的 左边界
{
next.start = cur.start;
next.left = cur.left;
next.right = cur.t - 1; //新的右边界
next.t = RMQ_query(next.left, next.right);
next.val = sum[next.t] - sum[next.start - 1];
pq.push(next);
} if(cur.t < cur.right) //当前取最值的位置 小于 当前和弦的 右边界
{
next.start = cur.start;
next.left = cur.t + 1; //新的左边界
next.right = cur.right;
next.t = RMQ_query(next.left, next.right);
next.val = sum[next.t] - sum[next.start - 1];
pq.push(next);
}
}
outn(ans);
return 0;
}

END.

洛谷P2048 [NOI2010]超级钢琴 题解的更多相关文章

  1. 洛谷 P2048 [NOI2010]超级钢琴 解题报告

    P2048 [NOI2010]超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为 ...

  2. [洛谷P2048] [NOI2010] 超级钢琴

    洛谷题目链接:[NOI2010]超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号 ...

  3. 洛谷 P2048 [NOI2010]超级钢琴 || Fantasy

    https://www.luogu.org/problemnew/show/P2048 http://www.lydsy.com/JudgeOnline/problem.php?id=2006 首先计 ...

  4. 洛谷 P2048 [NOI2010]超级钢琴(优先队列,RMQ)

    传送门 我们定义$(p,l,r)=max\{sum[t]-sum[p-1],p+l-1\leq t\leq p+r-1 \}$ 那么因为对每一个$p$来说$sum[p-1]$是一个定值,所以我们只要在 ...

  5. 洛谷P0248 [NOI2010] 超级钢琴 [RMQ,贪心]

    题目传送门 超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符 ...

  6. 【题解】P2048 [NOI2010]超级钢琴

    [题解][P2048 NOI2010]超级钢琴 一道非常套路的题目.是堆的套路题. 考虑前缀和,我们要是确定了左端点,就只需要在右端区间查询最大的那个加进来就好了.\(sum_j-sum_{i-1}​ ...

  7. P2048 [NOI2010]超级钢琴(RMQ+堆+贪心)

    P2048 [NOI2010]超级钢琴 区间和--->前缀和做差 多次查询区间和最大--->前缀和RMQ 每次取出最大的区间和--->堆 于是我们设个3元组$(o,l,r)$,表示左 ...

  8. BZOJ2006:[NOI2010]超级钢琴——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=2006 https://www.luogu.org/problemnew/show/P2048#su ...

  9. LGOJ P2048 [NOI2010]超级钢琴

    题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度为Ai,其中A ...

随机推荐

  1. ABP 04 用户的创建

    有这样一个问题,我忘记了密码,查了一下数据那张表,是加密了的,然后就有了这篇文章了. 往后台传的时候,还是传的明文. 请求的地址:/api/services/app/User/Create 用户还是挺 ...

  2. 如果设置Redis客户端的超时时长?

    客户端的超时时长分连接超时和读写超时,如果是基于hiredis的实现,则读写超时是合在一起的,同一参数控制. 在hiredis中,读写超时调用函数redisSetTimeout设置,可以看到没有区分读 ...

  3. Kafka的基本概念

    Kafka的前身是由LinkedIn开源的一款产品,2011年初开始开源,加入了 Apache 基金会,2012年从 Apache Incubator 毕业变成了 Apache 顶级开源项目. Top ...

  4. SDOI 二轮垫底鸡

    SDOI 二轮垫底鸡 day0 准备爆零 没啥好准备考试的,12.00出发,试机敲抄个ntt,在宾馆不知道颓啥. day1 爆零爬山 T1noip的题目也放到省选上. 第一档线段树?肯定不写,直接上1 ...

  5. 分类模型的评价指标Fscore

    小书匠深度学习 分类方法常用的评估模型好坏的方法. 0.预设问题 假设我现在有一个二分类任务,是分析100封邮件是否是垃圾邮件,其中不是垃圾邮件有65封,是垃圾邮件有35封.模型最终给邮件的结论只有两 ...

  6. Linux里使用rz和sz命令

    lrzsz是一个unix通信套件提供的X,Y,和ZModem文件传输协议,官网:http://freecode.com/projects/lrzsz/ windows 需要向centos服务器上传文件 ...

  7. RNN 的不变性

    卷积神经网络的不变性  不变性的实现主要靠两点:大量数据(各种数据):网络结构(pooling)不变性的类型  1)平移不变性  卷积神经网络最初引入局部连接和空间共享,就是为了满足平移不变性.    ...

  8. Gradle插件和Gradle对应表

    Gradle插件build.gradle文件的buildscript Gradlegradle/wrapper/gradle-wrapper.properties文件 AndroidStudio版本 ...

  9. Unity2D游戏开发之保卫萝卜

    保卫萝卜是2D塔防游戏里边的一个经典案例,这次去开发这个游戏,我们会尽力去实现和原版一样的功能,做好我们可以处理好的每一个游戏细节(比如塔攻击的集火目标优先攻击,与自动搜索,格子的三种处理逻辑,UI的 ...

  10. easyui datagrid生成序号列formatter

    var opts1; $('#datagrid_1').datagrid({ columns: [ [{ field: 'myNo', title: '序号', align: 'center', wi ...