P2048 [NOI2010] 超級鋼琴

liuboom發表於2024-12-06

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();
}

相關文章