[NOI2010] 超级钢琴
题目类型:RMQ+堆
传送门:>Here<
题意:给出一个长度为\(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 <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <cmath>
#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 <Chord> q;
inline void ST(){
for(int j = 1; (1 << j) <= N; ++j){
for(int i = 1; i + (1<<j) - 1 <= N; ++i){
mx[i][j] = Max(mx[i][j-1], mx[i+(1<<(j-1))][j-1]);
mp[i][j] = mp[i][j-1];
if(mx[i+(1<<(j-1))][j-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<<k)+1][k]);
q_pos = mp[L][k];
if(mx[R-(1<<k)+1][k] > mx[L][k]) q_pos = mp[R-(1<<k)+1][k];
}
int main(){
N = r, K = r, L = r, R = r;
for(int i = 1; i <= N; ++i){
a[i] = r;
sum[i] = sum[i-1] + a[i];
mx[i][0] = sum[i];
mp[i][0] = i;
}
ST();
for(int i = 1; i <= N; ++i){
pl = i + L - 1;
pr = i + R - 1;
if(pl > 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;
}

浙公网安备 33010602011771号