ABC352D 题解

ABC352D - Description

给你一个 \(n\) 的排列 \(a\),让你选出一个长度为 \(k\)\(a\) 的子序列 \(b=\left [ a_{p_1},a_{p_2},\cdots ,a_{p_k} \right ]\),使得 \(\min b_i +k-1=\max b_i\) 的同时控制 \(p_k-p_1\) 最小,求出这个最小值。

\(1\le n,k\le 2\times 10^5\)

ABC352D - Solution

注意到 \(a\) 数组本身就是排列,而要求选出排列就等于直接截取 \(a\) 的子串,这省去了中间的很多过程。先求出 \(c_{a_i}=i\),可以发现 \(c\)\(a\) 的一个置换,因此显然不影响答案。要让 \(b\) 数组构成一个排列 \(\{ a,a+1,\cdots ,a+k-1\}\),就是在 \(c\) 中选出一个长度为 \(k\) 的子串。而 \(p_k\) 就是这个子串中的最大 \(c_i\)\(p_1\) 就是最小 \(c_i\)。现在,答案就变成了下面式子的值:

\[\min_{i=1}^{n-k+1} (\max_{j=i}^{i+k-1} c_i - \min_{j=i}^{i+k-1} c_i) \]

单调队列扫描可以做到 \(O(n)\) 预处理。

#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,k,a[200005],c[200005],minn[200005],maxx[200005],ans=INT_MAX;
deque<int>q;
signed main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		c[a[i]]=i;
	}
//	for(int i=1;i<=n;i++){
//		cout<<c[i]<<" ";
//	}
//	cout<<endl;
	for(int i=1;i<=n;i++){
		while(!q.empty()&&i-q.front()>=k){
			q.pop_front();
		}
		while(!q.empty()&&c[q.back()]>c[i]){
			q.pop_back();
		}
		q.push_back(i);
		minn[i]=c[q.front()];
	}
	while(!q.empty()){
		q.pop_back();
	}
	for(int i=1;i<=n;i++){
		while(!q.empty()&&i-q.front()>=k){
			q.pop_front();
		}
		while(!q.empty()&&c[q.back()]<c[i]){
			q.pop_back();
		}
		q.push_back(i);
		maxx[i]=c[q.front()];
	}
	for(int i=1;i<=n-k+1;i++){
		int x=i+k-1;
		ans=min(ans,maxx[x]-minn[x]);
	}
	cout<<ans<<endl;
	return 0;
}
posted @ 2025-12-09 22:06  Creativexz  阅读(3)  评论(0)    收藏  举报