【RMQ】超级钢琴

来源:互联网 发布:左手定则洛伦兹力 知乎 编辑:程序博客网 时间:2024/04/29 10:46

超级钢琴:

  超级钢琴
【问题描述】
小Z 是一个小有名气的钢琴家,最近 C 博士送给了小Z一架超级钢琴,小 Z
希望能够用这架钢琴创作出世界上最美妙的音乐。
这架超级钢琴可以弹奏出 n个音符,编号为 1至n。第i个音符的美妙度为
Ai,其中Ai可正可负。
一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于 L
且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两
个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同
的。
小Z 决定创作一首由 k个超级和弦组成的乐曲,为了使得乐曲更加动听,小
Z要求该乐曲由k个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包
含的所有超级和弦的美妙度之和。小 Z想知道他能够创作出来的乐曲美妙度最大
值是多少。
【输入格式】
输入文件名为 piano.in。
输入文件第一行包含四个正整数 n, k, L, R。其中 n为音符的个数,k为乐
曲所包含的超级和弦个数, L和R分别是超级和弦所包含音符个数的下限和上限。 
接下来n行,每行包含一个整数 Ai,表示按编号从小到大每个音符的美妙度。
【输出格式】

输出文件为 piano.out。
输出文件只有一个整数,表示乐曲美妙度的最大值。
【样例输入】
4 3 2 3
3
2
-6
8
【样例输出】
11

【样例说明】
共有5种不同的超级和弦:
1. 音符 1 ~ 2,美妙度为 3 + 2 = 5
2. 音符 2 ~ 3,美妙度为 2 + (-6) = -4
3. 音符 3 ~ 4,美妙度为(-6) + 8 = 2
4. 音符 1 ~ 3,美妙度为 3 + 2 + (-6) = -1
5. 音符 2 ~ 4,美妙度为 2 + (-6) + 8 = 4
最优方案为:乐曲由和弦 1,和弦3,和弦 5组成,美妙度为5 + 2 + 4 = 11。
【运行时限】
2秒。
【运行空限】
512M。
【数据规模和约定】
总共 10个测试点,数据范围满足:


题目大意:

取前K个最大的连续区间和,区间长度在[L,R]之间。

使用RMQ的ST算法和堆来实现。


注意这里有一个单调性,是我一开始没有领会到的:当左边界固定的时候,随着右边界的移动,区间和的大小和右边界的前缀和是成正相关的。

因此,当左边界固定的时候,要求一个最大的区间和,就在所有右边界的可能取值中,找一个前缀和最大的。


这时候,RMQ就隆重登场了。以前没有想过,最大区间和和区间最大值可以这样转换呀。。其实很简单的,只是我转不过弯。

因此我们需要找到对于所有的i,[i,i+L-1]、[i,i+L]……[i,i+R-1]这些区间之中的最大值,总共有n个。

我们用堆来维护比较好。

注意:当从堆中取出一个最大的时候,可能对于这个i,[i,i+L-1]、[i,i+L]……[i,i+R-1]这些区间之中的次大值,会比对于其他i的最大值更大。

因此我们需要将这些区间重新放回去,除开[i,i+Pos-1],假设i+Pos-1是原来的最大值。实现方法参见Wjj的博客Whjpji。http://blog.csdn.net/whjpji/article/details/7375438


这道题确实很巧妙呀我觉得。

两个版本最后两个点都超时,想不出办法来了。

手写堆版:

#include <iostream>#include <cstdio>//#include <queue>#define MAX(a,b) (a>b?a:b)#define MIN(a,b) (a<b?a:b)typedef long long ll;//using std::priority_queue;int n;int K;int L;int R;ll f[500010][25];ll sum[500010];int RMQ(int l,int r);int lg2(ll a);struct node{int l;int a;int b;int pos;ll maxsum;node(int ll,int aa,int bb):l(ll),a(aa),b(bb){pos = RMQ(ll+aa-1,ll+bb-1) - l + 1;maxsum = sum[l + pos - 1] - sum[l-1];}bool operator<(const node& n2)const{return  maxsum > n2.maxsum;}node(){}};node heap[1000010];int SIZE = 0;inline int RMQ(int l,int r){int tmp = 0;int length = r-l+1;while (length>>=1ll)tmp++;if (sum[f[l][tmp]] > sum[f[r-(1<<tmp)+1][tmp]])return f[l][tmp];elsereturn f[r-(1<<tmp)+1][tmp];}inline void adjust_down(int l){while ((l<<=1) < SIZE+1){if (l<SIZE && heap[l+1]<heap[l]) l++;if (heap[l]<heap[l>>1]) std::swap(heap[l],heap[l>>1]);else break;}}inline void pop(){heap[1] = heap[SIZE];SIZE--;adjust_down(1);} inline void adjust_up(int l){while (l > 1){if (heap[l]<heap[l>>1])std::swap(heap[l],heap[l>>1]);else break;l >>= 1;}}inline void push(node l){heap[++SIZE] = l;adjust_up(SIZE);}inline int getint(){    int res = 0; char tmp; bool sgn = 1;    do tmp = getchar();    while (!isdigit(tmp) && tmp != '-');    if (tmp == '-')    {        sgn = 0;        tmp = getchar();    }    do res = (res << 1) + (res << 3) + tmp - '0';    while (isdigit(tmp = getchar()));    return sgn ? res : -res;}int main(){freopen("piano.in","r",stdin);freopen("piano.out","w",stdout);n = getint();K = getint();L = getint();R = getint();for (int i=1;i<n+1;i++){int tmp = getint();sum[i] = sum[i-1] + (ll)tmp;f[i][0] = i;}for (int j=1;(1<<j)<=n;j++){for (int i=1;i+(1<<j)-1<n+1;i++){if (sum[f[i][j-1]] > sum[f[i+(1<<(j-1))][j-1]])f[i][j] = f[i][j-1];elsef[i][j] = f[i+(1<<(j-1))][j-1];}}for (int i=1;i+L-1<n+1;i++){push(node(i,L,MIN(R,n-i+1)));}ll ans = 0; while (K--){node tmp = heap[1];ans += tmp.maxsum;pop();if (tmp.a < tmp.pos){push(node(tmp.l,tmp.a,tmp.pos-1));}if (tmp.pos < tmp.b){push(node(tmp.l,tmp.pos+1,tmp.b));}}printf("%I64d",ans);return 0;}
priority_queue版

#include <iostream>#include <cstdio>#include <queue>#define MAX(a,b) (a>b?a:b)#define MIN(a,b) (a<b?a:b)typedef long long ll;using std::priority_queue;long n;long K;long L;long R;ll f[500010][25];ll sum[500010];long RMQ(long l,long r);long lg2(ll a);struct node{long l;long a;long b;long pos;ll maxsum;node(long ll,long aa,long bb):l(ll),a(aa),b(bb){pos = RMQ(ll+aa-1,ll+bb-1) - l + 1;maxsum = sum[l + pos - 1] - sum[l-1];}bool operator<(const node& n2)const{return  maxsum < n2.maxsum;}};priority_queue<node> heap;inline long lg2(ll a){long ans = 0;while (a>>=1ll)ans++;return ans;}long RMQ(long l,long r){long tmp = lg2(r-l+1);if (sum[f[l][tmp]] > sum[f[r-(1<<tmp)+1][tmp]])return f[l][tmp];elsereturn f[r-(1<<tmp)+1][tmp];}int main(){freopen("piano.in","r",stdin);freopen("piano.out","w",stdout);scanf("%ld%ld%ld%ld",&n,&K,&L,&R);for (long i=1;i<n+1;i++){long tmp;scanf("%ld",&tmp);sum[i] = sum[i-1] + (ll)tmp;f[i][0] = i;}for (long j=1;(1<<j)<=n;j++){for (long i=1;i+(1<<j)-1<n+1;i++){if (sum[f[i][j-1]] > sum[f[i+(1<<(j-1))][j-1]])f[i][j] = f[i][j-1];elsef[i][j] = f[i+(1<<(j-1))][j-1];}}for (long i=1;i+L-1<n+1;i++){heap.push(node(i,L,MIN(R,n-i+1)));}ll ans = 0; while (K--){node tmp = heap.top();ans += tmp.maxsum;heap.pop();if (tmp.a < tmp.pos){heap.push(node(tmp.l,tmp.a,tmp.pos-1));}if (tmp.pos < tmp.b){heap.push(node(tmp.l,tmp.pos+1,tmp.b));}}std::cout << ans;return 0;}