【RMQ】超级钢琴

超级钢琴:

  超级钢琴
【问题描述】
小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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值