AT_abc352_d

看到题目后,第一反应便是暴力枚举确定 \(i\)。但是看到了 \(N \le 2 \times 10^5\),这种想法便不合适了。观察题目第二个条件,不难发现其实真正合法的方案很少。于是可以转变方向,枚举题目要求的排列集合

想到这步,接下来就不难了。确定好原排列中被选的数后,需要求出它们的下标的最小值和最大值。又由于这些数本身是连续的,因此可以用这些数作为下标,存储它们原先的下标。这样做就把这些下标集中在一起了,转换为区间最值查询问题,用各种数据结构维护即可。

以下采用 ST 表实现,时间复杂度 \(O( n \log n )\)

#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long

using namespace std;

int n,k,p,ans,a[1000001],st1[1000001][20],st2[1000001][20],lg[1000001];

void rd( void )
{
	for( int j = 1 ; j <= 19 ; j ++ )
		for( int i = 1 ; i + ( 1 << ( j - 1 ) ) <= n ; i ++ )
		{
			st1[i][j] = max( st1[i][j - 1] , st1[i + ( 1 << ( j - 1 ) )][j - 1] );
			st2[i][j] = min( st2[i][j - 1] , st2[i + ( 1 << ( j - 1 ) )][j - 1] );
		}
}

int query( int l , int r )
{
	int d = lg[r - l + 1];
	int ans1 = max( st1[l][d] , st1[r - ( 1 << d ) + 1][d] );
	int ans2 = min( st2[l][d] , st2[r - ( 1 << d ) + 1][d] );
	return abs( ans1 - ans2 );
}

signed main()
{
	cin >> n >> k;
	for( int i = 2 ; i <= n ; i ++ )
		lg[i] = lg[i >> 1] + 1;
	for( int i = 1 ; i <= n ; i ++ )
	{
		cin >> p;
		a[p] = i;
		st1[p][0] = st2[p][0] = i;
	}
	rd();
	ans = n;
	for( int i = 1 ; i + k - 1 <= n ; i ++ )
		ans = min( ans , query( i , i + k - 1 ) );
	cout << ans;
	return 0;
}
posted @ 2024-05-18 17:31  liyilang2021  阅读(1)  评论(0编辑  收藏  举报