超级钢琴:
【问题描述】
小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];
else
return 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];
else
f[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];
else
return 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];
else
f[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;
}