P2048 [NOI2010] 超级钢琴

P2048 [NOI2010] 超级钢琴

[NOI2010] 超级钢琴

题目描述

小 Z 是一个小有名气的钢琴家,最近 C 博士送给了小 Z 一架超级钢琴,小 Z 希望能够用这架钢琴创作出世界上最美妙的音乐。

这架超级钢琴可以弹奏出 \(n\) 个音符,编号为 \(1\)\(n\)。第 \(i\) 个音符的美妙度为 \(A_i\),其中 \(A_i\) 可正可负。

一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于 \(L\) 且不多于 \(R\)。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。

小 Z 决定创作一首由 \(k\) 个超级和弦组成的乐曲,为了使得乐曲更加动听,小 Z 要求该乐曲由 \(k\) 个不同的超级和弦组成。我们定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和。小 Z 想知道他能够创作出来的乐曲美妙度最大值是多少。

输入格式

输入第一行包含四个正整数 \(n, k, L, R\)。其中 \(n\) 为音符的个数,\(k\) 为乐曲所包含的超级和弦个数,\(L\)\(R\) 分别是超级和弦所包含音符个数的下限和上限。

接下来 \(n\) 行,每行包含一个整数 \(A_i\),表示按编号从小到大每个音符的美妙度。

输出格式

输出只有一个整数,表示乐曲美妙度的最大值。

所有数据满足:\(-1000 \leq A_i \leq 1000\)\(1 \leq L \leq R \leq n\) 且保证一定存在满足要求的乐曲。

------------------------------------------------------------------------------------------------------
本来我以为我写过类似的题
P5283 [十二省联考 2019] 异或粽子
就能把轻松把这题A掉,但没学过RMQ的我还是年轻了qwq

首先介绍一下RMQ:

RMQ 是英文 Range Maximum/Minimum Query 的缩写,表示区间最大(最小)值。——OI Wiki

用我的话说这东西就是用倍增的思想维护一个st表

RMQ Code:

void get_len(){for(int i=0;i<lg;i++)len[i]=1<<i;}
void get_st()
{
	get_len();
	for(int i=1;i<=n;i++)st[i][0]=i;
	for(int j=1;len[j]<=n;j++)
	{
		for(int i=1;i+len[j-1]-1<=n;i++)
		{
			int L=st[i][j-1],R=st[i+len[j-1]][j-1];
			st[i][j]= sum[L]>sum[R] ? L : R;
		}
	
	}
}

但有些不同的是,这里的st数组我维护的是sum值最大的的下标(对这题十分有用)

solution:

学完RMQ之后,本题的思路瞬间明确了:
我们维护一个前缀和,然后钦定一个点x,查询在合法长度区间 [l,r] 内, sum[pos] 的最大值,然后这个点的贡献显然就是 sum[pos]-sum[x-1]

至于维护贡献的方法那自然就是优先队列了捏~

在题面中可观察到:

两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的。

然后我们仿照
P5283 [十二省联考 2019] 异或粽子
的思路:
在每个(x,[l,r])用完之后将其拆分为(x,[l,pos-1]),(x,[pos+1,r])

然后这题就做完了

那我们学的RMQ去哪了?
当然是维护“在合法长度区间 [l,r] 内, sum[pos] 的最大值”去了呗qwq

话说这个维护应该可以用线段树,可是多一个log还不太好写
况且我还懒

Code:

#include<bits/stdc++.h>
#define int long long
const int N=5e5+5;
const int lg=20;
using namespace std;
int a[N],sum[N],st[N][lg],len[lg];
int n,k,L_lim,R_lim;
void get_len(){for(int i=0;i<lg;i++)len[i]=1<<i;}//由于我觉得总写(1<<i)太难看 
void get_st()
{
	get_len();
	for(int i=1;i<=n;i++)st[i][0]=i;
	for(int j=1;len[j]<=n;j++)
	{
		for(int i=1;i+len[j-1]-1<=n;i++)
		{
			int L=st[i][j-1],R=st[i+len[j-1]][j-1];//注意st表中的边界问题 
			st[i][j]= sum[L]>sum[R] ? L : R;
		}
	
	}
}
int query(int l,int r)//
{
	int ll=log2(r-l+1);
	int L=st[l][ll],R=st[r-len[ll]+1][ll];
	return sum[L]>sum[R] ? L : R;
}
struct Node{
	int x,l,r,pos,val;
	Node(int x_=0,int l_=0,int r_=0)
	{
		x=x_,l=l_,r=r_;
		pos=query(l,r);
		val=sum[pos]-sum[x-1];
	}
	bool operator <(const Node &n1)const{
		return n1.val>val;
	}
};
priority_queue<Node> Q;
void work()
{
	int ans=0;
	cin>>n>>k>>L_lim>>R_lim;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	get_st();
	for(int i=1;i+L_lim-1<=n;i++)
	{
		int ll=i+L_lim-1,rr=min(i+R_lim-1,n);
		Q.push(Node(i,ll,rr));
	};
	for(int i=1;i<=k;i++)
	{
		Node u=Q.top();Q.pop();
		ans+=u.val;
		if(u.l<=u.pos-1)Q.push(Node(u.x,u.l,u.pos-1));
		if(u.pos+1<=u.r)Q.push(Node(u.x,u.pos+1,u.r));
	}
	printf("%lld",ans);
}
#undef int
int main()
{
	freopen("P2048_1.in","r",stdin);//freopen("P2048.out","w",stdout);
	work();
}
posted @ 2024-12-06 11:44  liuboom  阅读(51)  评论(0)    收藏  举报