Link
思维+二分 1800
题意
给定一个数组 a a a,请给定一个值域范围[x, y],并将其分为 k k k 段连续的子数组,使得每一段子数组中,在范围内的数总是严格大于不在范围内的数的个数。请最小化 y − x y - x y−x ,并输出对应的[x,y]的数组划分方案。
思路
先考虑对于给定的
[
x
,
y
]
[x,y]
[x,y] 如何判断其能否分为满足条件的
k
k
k 段?
容易想到能分为
k
k
k 段当且仅当在区间
[
x
,
y
]
[x,y]
[x,y]内的数
c
n
t
i
n
cnt_{in}
cntin 与不在区间内的数数量
c
n
t
o
u
t
cnt_{out}
cntout 满足
c
n
t
i
n
+
k
≥
c
n
t
o
u
t
cnt_{in} + k \geq cnt_{out}
cntin+k≥cntout
接下来问题就简单了,二分答案即可。感觉做法蛮多的,我是先对
a
i
a_i
ai 由小到大排序,然后对
i
∈
[
1
,
n
]
i \in[1, n]
i∈[1,n]取
x
x
x 为
a
i
a_i
ai,二分
y
y
y,维护一个
y
−
x
y-x
y−x的最小值以及对应的
x
,
y
x,y
x,y,最后遍历一遍排序前数组输出即可。
代码
int n, k;
int a[maxn];
int b[maxn];
bool ok(int l, int x) {
int q = upper_bound(b + 1, b + n + 1, x) - b;
int yes = q - (l - 1) - 1;
int no = n - yes;
if(yes >= no + k) return true;
return false;
}
void solve() {
cin >> n >> k;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
b[i] = a[i];
}
sort(b + 1, b + n + 1);
int res = INF;
int L, R;
for(int i = 1; i <= n; i++) {
int l = b[i], r = b[n];
int ans = INF;
while(l <= r) {
int mid = (l + r) >> 1;
if(ok(i, mid)) {
ans = mid;
r = mid - 1;
}
else l = mid + 1;
}
if(ans - b[i] <= res) {
res = ans - b[i];
L = b[i]; R = ans;
}
}
cout << L << ' ' << R << endl;
int l = 1;
int cnt1 = 0, cnt2 = 0;
int cnt3 = 0;
for(int i = 1; i <= n; i++) {
if(a[i] >= L && a[i] <= R)
cnt1++;
else cnt2++;
if(cnt1 > cnt2 && cnt3 < k - 1) {
cout << l << ' ' << i << endl;
l = i + 1;
cnt1 = cnt2 = 0;
cnt3++;
}
}
cout << l << ' ' << n << endl;
}