洛谷P2048 [NOI2010]超级钢琴 题解
2019/11/14 更新日志:
近期发现这篇题解有点烂,更新一下,删繁就简,详细重点。代码多加了注释。就酱紫啦!
正解步骤
我们需要先算美妙度的前缀和,并初始化RMQ。
循环 \(i\) 从 \(1\) 到 \(n\) ,因为以i为起点的 和弦 终点必定是 \(i + L - 1\) 到 \(i + R - 1\) 之间,所以只要在区间内用RMQ取 超级和弦 ,并加入以美妙度从小排到大的优先队列中。
取出堆顶元素,将美妙度加入 \(ans\) ,并将元素切为从 (当前元素的左边界 到 当前元素终点 - 1) 和 (\(当前元素终点 + 1 到 当前元素右边界\)) 两个部分,并再次加入优先队列,依次进行 \(k\) 次。
输出答案即可
为什么要使用前缀和 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]超级钢琴 题解的更多相关文章
- 洛谷 P2048 [NOI2010]超级钢琴 解题报告
P2048 [NOI2010]超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为 ...
- [洛谷P2048] [NOI2010] 超级钢琴
洛谷题目链接:[NOI2010]超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号 ...
- 洛谷 P2048 [NOI2010]超级钢琴 || Fantasy
https://www.luogu.org/problemnew/show/P2048 http://www.lydsy.com/JudgeOnline/problem.php?id=2006 首先计 ...
- 洛谷 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]$是一个定值,所以我们只要在 ...
- 洛谷P0248 [NOI2010] 超级钢琴 [RMQ,贪心]
题目传送门 超级钢琴 题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符 ...
- 【题解】P2048 [NOI2010]超级钢琴
[题解][P2048 NOI2010]超级钢琴 一道非常套路的题目.是堆的套路题. 考虑前缀和,我们要是确定了左端点,就只需要在右端区间查询最大的那个加进来就好了.\(sum_j-sum_{i-1} ...
- P2048 [NOI2010]超级钢琴(RMQ+堆+贪心)
P2048 [NOI2010]超级钢琴 区间和--->前缀和做差 多次查询区间和最大--->前缀和RMQ 每次取出最大的区间和--->堆 于是我们设个3元组$(o,l,r)$,表示左 ...
- BZOJ2006:[NOI2010]超级钢琴——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=2006 https://www.luogu.org/problemnew/show/P2048#su ...
- LGOJ P2048 [NOI2010]超级钢琴
题目描述 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐. 这架超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度为Ai,其中A ...
随机推荐
- 利用python画小猪佩奇
import turtle as t t.pensize(4) t.hideturtle() t.colormode(255) t.color((255,155,192),"pink&quo ...
- windows下redis的配置文件(redis.windows.conf)
#redis的配置 #Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 daemonize yes #当Redis以守护进程方式运行时,Redis默认会把pid写入 ...
- Fluent设置充分发展湍流入口(利用profile)
计算模型: 物性参数为: 密度:100kg/m3 粘度系数:0.003333kg/(m·s) 原视频下载地址: https://pan.baidu.com/s/1W3n_K-dZCVMF7M63wV2 ...
- 多浏览器书签同步插件EverSync
有时上网时会遇到浏览器不能正常显示的问题.(比如我的火狐浏览器无法正确显示微信公众号管理后台,在chrome上可以正常显示),所以我的电脑里安装了chrome和firefox两个浏览器.但是时间长了, ...
- Python 下载依赖包环境经常失败超时解决方法
人生苦短,我用python!为什么很多人喜欢用python,因为包多呀,各种调包.但是调包有的时候也调的闹心,因为安装包不是失败就是很慢,很影响自己的工作进度,这里给出一个pip快速安装工具包的办法, ...
- Oracle 日期各个部分常用写法
--1.日期的各部分的常用的的写法 --- --1) 取时间点的年份的写法: SELECT TO_CHAR(SYSDATE,'YYYY') FROM DUAL; --结果:2019 --2) 取时间点 ...
- SpringBoot入门-集成mybatis(四)
pom.xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spr ...
- mysql中字符串的隐藏字符处理
三步解决mysql字符串的隐藏字符: 1. 隐藏字符导致字符串长度边长,用mysql 自带的 Hex函数让隐藏字符显示真身, 2. 可以拿到隐藏字符的16进制码,然后用windows自带的计算器转化成 ...
- Elasticsearch6.2.1安装elasticsearch-sq插件
参考 https://github.com/NLPchina/elasticsearch-sql 1.下载插件 wget https://github.com/NLPchina/elasticsear ...
- Python 工匠:使用数字与字符串的技巧学习笔记
#Python 工匠:使用数字与字符串的技巧学习笔记#https://github.com/piglei/one-python-craftsman/blob/master/zh_CN/3-tips-o ...