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