题目
题意:给定长度为
n
n
n的数组
a
a
a和
k
k
k,其中
1
<
=
a
i
<
=
n
1<=a_i<=n
1<=ai<=n现在要求找出区间长度最小的
x
,
y
x,y
x,y,将数组
a
a
a划分成k个子数组,其中
a
i
a_i
ai位于这k个子数组的其中之一。要求每个子数组中,位于
[
x
,
y
]
[x,y]
[x,y]范围内的元素,多于不在
[
x
,
y
]
[x,y]
[x,y]范围内的元素。
现在构造出x,y,以及这k个子数组的区间。
官方题解
思路:考虑x,y已知的情况。对于每个
a
i
a_i
ai,如果
x
<
=
a
i
<
=
y
x<=a_i<=y
x<=ai<=y,则+1,否则记为-1。那么要求每个子数组累计得分要为正。因此,我们初始得分为0,当得分首次大于0时,则截取当前区间,并开始取新的区间,得分重新归零。注意最后一个区间(即第k个子数组)保留。这里用的是前缀和的思想。
如何确定x,y。不妨设上述得分数组为 b b b,前缀和数组为 p r e pre pre。我们可以发现 b b b取值为1,-1;从而 p r e pre pre相邻元素差值绝对值为1,即 ∣ p r e i − 1 − p r e i ∣ = 1 |pre_{i-1}-pre_i|=1 ∣prei−1−prei∣=1。要使按照上述贪心思路,能找到k组子数组,说明 p r e n pre_n pren至少要有k,即 p r e n > = k pre_n>=k pren>=k;此外,由于 p r e pre pre是连续变化的,我们必然可以找到 p r e pre pre取值为1,2,…,k的下标,我们取它们出现的第一次位置即可。
要使
p
r
e
n
>
=
k
pre_n>=k
pren>=k,则有
2
∗
∑
i
=
1
n
[
x
<
=
a
i
<
=
y
]
−
n
>
=
k
2*\sum_{i=1}^{n}[x<=a_i<=y] - n>=k
2∗∑i=1n[x<=ai<=y]−n>=k,有
∑
i
=
1
n
[
x
<
=
a
i
<
=
y
]
>
=
⌈
n
+
k
2
⌉
\sum_{i=1}^{n}[x<=a_i<=y] >=\lceil \frac{n+k}{2}\rceil
∑i=1n[x<=ai<=y]>=⌈2n+k⌉
我们可以对数组排序,枚举x从小到大,查看对应有
⌈
n
+
k
2
⌉
\lceil \frac{n+k}{2}\rceil
⌈2n+k⌉数量的y的最小取值,所有的情况中,取
y
−
x
y-x
y−x最小的。
官方代码,写的真好
#include<bits/stdc++.h>
using namespace std;
int main() {
int tc;
cin >> tc;
while( tc-- ){
int n, k;
cin >> n >> k;
vector<int> a(n), sorted_a(n);
for(int i=0; i<n; i++){
cin >> a[i];
sorted_a[i] = a[i];
}
sort(sorted_a.begin(),sorted_a.end());
int req_sum = ( n + k + 1 ) / 2;
pair<int,pair<int,int>> ans = { n + 1 , { -1 , -1 } };
for(int i=0; i+req_sum-1<n; i++)
ans = min( ans , { sorted_a[i+req_sum-1] - sorted_a[i] , { sorted_a[i] , sorted_a[i+req_sum-1] } } );
cout << ans.second.first << ' ' << ans.second.second << '\n';
int subarrays_found = 0, curr_sum = 0;
int last_uncovered = 1;
for(int i=0; i<n; i++){
if( a[i] >= ans.second.first && a[i] <= ans.second.second ) curr_sum ++;
else curr_sum --;
if( curr_sum > 0 && subarrays_found + 1 < k ){
cout << last_uncovered << ' ' << ( i + 1 ) << '\n';
last_uncovered = i + 2;
subarrays_found ++;
curr_sum = 0;
}
}
subarrays_found ++;
cout << last_uncovered << ' ' << n << '\n';
}
return 0;
}