假设经过 m m m 次操作后,剩余序列的值域为 [ l , r ] [l,r] [l,r] ,那么小于 l l l 的数和大于 r r r 的数一定被操作了。
设原序列中,小于 l l l 的数有 u u u 个,大于 r r r 的数有 v v v 个。
那么最小操作次数为 u + v + m i n ( u , v ) u + v + min(u, v) u+v+min(u,v) 。
证明:
不妨假设 u < v u < v u<v ,进行 u u u 次操作将原序列的最小值变为 l l l ,再进行 u + v u + v u+v 次操作将原序列的最大值变为 r r r 。
那么操作次数为 u + v + u u + v + u u+v+u 即为 u + v + m i n ( u , v ) u + v + min(u,v) u+v+min(u,v) 。
通俗一点理解为,先将数量小的一端变为另一端,然后再变回来。
具体操作:
先对序列升序排序。
枚举所有的 a i a_i ai 作为 l l l ,找到最小的 a j a_j aj 作为 r r r ,此时极值为 r − l r-l r−l 。
由于 j ≥ i j \ge i j≥i ,因为 i i i 是递增的,所以 j j j 是单调不减的,因此时间复杂度瓶颈是排序 ( n l o g n nlogn nlogn) 。
如何找到 j j j 呢?
因为有 ( i − 1 ) + ( n − j ) + m i n ( i − 1 , n − j ) ≤ m (i-1)+(n-j)+min(i-1,n-j) \le m (i−1)+(n−j)+min(i−1,n−j)≤m ,所以让 j j j 从 m a x ( i , j ) max(i,j) max(i,j) 开始循环,找到第一个满足条件的 j j j 即可。
// #pragma GCC optimize(3)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #include
// #include
#define endl '\n'
#define x first
#define y second
#define fi first
#define se second
#define PI acos(-1)
// #define PI 3.1415926
#define LL long long
#define INF 0x3f3f3f3f
#define lowbit(x) (-x&x)
#define PII pair<int, int>
#define ULL unsigned long long
#define PIL pair<int, long long>
#define all(x) x.begin(), x.end()
#define mem(a, b) memset(a, b, sizeof a)
#define rev(x) reverse(x.begin(), x.end())
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int N = 1e5 + 10;
int n, m;
int a[N], b[N];
int res = 2 * INF;
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) {
cin >> a[i];
}
sort(a + 1, a + 1 + n);
int j = 1;
for (int i = 1; i <= min(n, m + 1); i ++ ) {
j = max(j, i);
while ((i - 1) + (n - j) + min(i - 1, n - j) > m) ++ j;
res = min(res, a[j] - a[i]);
}
cout << res << endl;
}
int main() {
IOS;
solve();
return 0;
}