題解:洛谷 P10878 [JRKSJ R9] 在相思樹下 III

Jerrycyx發表於2024-09-01

原題連結

解析

在操作一時,最小值如果在最後一位,其無法更新任何數,會被刪除;否則不在最後一位時一定會被其右側更大的數更新。所以在操作一時,最小值一定會被更新掉。

同理,在操作二時,最大值一定會被更新掉。

由此,操作一決定了答案的下限,操作二決定了答案的上限。

所以可以得出貪心策略:先進行 \(m\) 次操作一,後進行 \(n-m-1\) 次操作二

進行完操作一以後,很明顯如果只進行操作二,那麼最後剩下的一定是剩下數的最小值。

\(m\) 次操作一的時間複雜度為 \(O(NM)\),可以得 \(40\) 分,但操作一的過程還可以最佳化。

因為 \(m\) 次操作一可以使 \(a_i\) 影響到 \(a_{i-1},a_{i-2},\cdots,a_{i-m}\),所以反過來,\(a_i\) 在經過 \(m\) 次操作一以後被 \(a_{i+1},a_{i+2},\cdots,a_{i+m}\) 影響,\(a_i\) 即為 \(\max\{a_j\}\),其中 \(j \in [i+1,i+m]\)

問題轉化成了“對於每個數,求它右邊 \(m\) 個數的最大值”,可以用優先佇列維護。

程式碼:

40 Pts 程式碼

#include<cstdio>
#include<algorithm>
using namespace std;

const int N=1e6+5;
int n,m,a[N];

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n-i;j++)
			a[j]=max(a[j],a[j+1]);
	}
	int ans=0x3f3f3f3f;
	for(int i=1;i<=n-m;i++)
		ans=min(ans,a[i]);
	printf("%d\n",ans);
	return 0;
}

100Pts 程式碼

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;

const int N=1e6+5;
int n,m,a[N],b[N];
deque<int> q;

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		if(!q.empty() && i-q.front()>m) q.pop_front();
		while(!q.empty() && a[q.back()]<a[i]) q.pop_back();
		q.push_back(i);
		if(i>=m+1) b[i-m]=a[q.front()];
	}
	int ans=0x3f3f3f3f;
	for(int i=1;i<=n-m;i++)
		ans=min(ans,b[i]);
	printf("%d\n",ans);
	return 0;
}

相關文章