博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[NOI2010] 超级钢琴
阅读量:4940 次
发布时间:2019-06-11

本文共 2389 字,大约阅读时间需要 7 分钟。

题目类型:RMQ+堆

传送门:><

题意:给出一个长度为\(N\)的序列\(a\),对于每一个\(i\)作为和弦的起点,长度可以是\(L \rightarrow R\)。问所有和弦中最大的\(K\)个和弦的和是多少

解题思路

先考虑暴力的做法:枚举左端点,再依次枚举右端点,统计答案。排序后累计前\(K\)

在确定左端点\(x\)时,有没有快速的方法求出其所有右端点中能与它构成的所有和弦中的最大值呢?维护一个前缀和\(sum[i]\),则有最大值为\[Max\{sum[j] \ | \ _{(i+L-1 \leq j \leq i+R-1)}\}-sum[x-1] \]由此将问题转化了\(sum\)区间最大值问题,用\(ST\)表求解即可

我们对于每一个左端点,都用如上方法求解出最大值,那么将会得到\(N\)个最大值(当然如果无法再往右扩散到规定长度的和弦可以不用考虑)。这\(N\)个最大值中的最大值一定是所有和弦的最大值。因此它肯定作为答案的一部分。记其左端点为\(i\),右端点位置为\(t\)。既然和弦\([i,t]\)不用考虑了,但是答案还有可能存在于\([i,i+L-1 \rightarrow t-1]\)\([i,t+1 \rightarrow i+R-1]\)这两部分内。于是他们就作两个部分加入到我们的考虑范围内。很容易发现剩下的\(N-1\)个最大值以及这两个区间的最大值中的最大值,依然是除了刚才剔除的\([i,t]\)以外的最大值。

因此,我们只需要维护一个大根堆,每一次累积堆顶的答案,然后将堆顶分裂为两个部分重新插入堆。这样的进行\(K\)轮就能够得到答案。

Code

注意在插入堆时,需要特判左右边界重合或左边界大于右边界的情况。

/*By DennyQi 2018*/#include 
#include
#include
#include
#include
#define r read()using namespace std;typedef long long ll;const int MAXN = 500010;const int INF = 1061109567;inline int Max(const int a, const int b){ return (a > b) ? a : b; }inline int Min(const int a, const int b){ return (a < b) ? a : b; }inline int read(){ int x = 0; int w = 1; register char c = getchar(); for(; c ^ '-' && (c < '0' || c > '9'); c = getchar()); if(c == '-') w = -1, c = getchar(); for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;}struct Chord{ int X,L,R,T,P;};inline bool operator < (const Chord& a, const Chord& b){ return a.T < b.T;}ll Ans;int N,K,L,R,pl,pr,q_pos,q_mx,_x,_l,_r,_t,_p;int a[MAXN],sum[MAXN],mx[MAXN][20],mp[MAXN][20];priority_queue
q;inline void ST(){ for(int j = 1; (1 << j) <= N; ++j){ for(int i = 1; i + (1<
mx[i][j-1]){ mp[i][j] = mp[i+(1<<(j-1))][j-1]; } } }}inline void query(int L, int R){ int k = log(R-L+1) / log(2); q_mx = Max(mx[L][k], mx[R-(1<
mx[L][k]) q_pos = mp[R-(1<
N) break; if(pr > N) pr = N; query(pl, pr); q.push((Chord){i,pl,pr,q_mx-sum[i-1],q_pos}); } for(int k = 1; k <= K; ++k){ Ans += 1LL * q.top().T; _x = q.top().X, _l = q.top().L, _r = q.top().R, _t = q.top().T, _p = q.top().P; if(_l <= _p-1){ query(_l, _p-1); q.push((Chord){_x, _l, _p-1, q_mx - sum[_x-1], q_pos}); } if(_p+1 <= _r){ query(_p+1, _r); q.push((Chord){_x, _p+1, _r, q_mx - sum[_x-1], q_pos}); } q.pop(); } printf("%lld", Ans); return 0;}

转载于:https://www.cnblogs.com/qixingzhi/p/9515662.html

你可能感兴趣的文章
performSelector的方法
查看>>
redis
查看>>
BZOJ1645 [Usaco2007 Open]City Horizon 城市地平线
查看>>
配置IIS
查看>>
单例模式详解
查看>>
电商项目(下)
查看>>
[NOIP2015] 子串
查看>>
NSSet和NSArray区别与方法总结
查看>>
Python列表 元组 字典 集合
查看>>
foreach遍历数组、数组的转置与方阵的迹
查看>>
Still unable to dial persistent://blog.csdn.net:80 after 3 attempts
查看>>
HTML超文本标记语言(九)——表单输入类型
查看>>
基于busybox制作mini2440根文件系统及使用nfs挂载
查看>>
信道容量及信道编码原理学习
查看>>
关于信息论中熵、相对熵、条件熵、互信息、典型集的一些思考
查看>>
浅谈独立特征(independent features)、潜在特征(underlying features)提取、以及它们在网络安全中的应用...
查看>>
从随机过程的熵率和马尔科夫稳态过程引出的一些思考 - 人生逃不过一场马尔科夫稳态...
查看>>
《A First Course in Abstract Algebra with Applications》-chaper1-数论-关于素数
查看>>
ORA-3136
查看>>
算法笔记_145:拓扑排序的应用(Java)
查看>>