题意:
给定一个长度为
n
n
n 的序列
a
a
a,以及一个数
k
k
k 。
每次你可以选择以下两个操作之一:
- 操作 1:选择一个元素 a i = a i − 1 a_i=a_i-1 ai=ai−1
- 操作 2:选择两个元素 a i a_i ai 和 a j a_j aj,使得 a i = a j a_i=a_j ai=aj
问至少多少次操作可以使得 ∑ i = 1 n a i ≤ k \sum\limits_{i=1}^n a_i \leq k i=1∑nai≤k
数据范围:
1
≤
n
≤
2
×
1
0
5
1\leq n\leq 2\times 10^5
1≤n≤2×105
1
≤
k
≤
1
0
15
1\leq k\leq 10^{15}
1≤k≤1015
1
≤
a
i
≤
1
0
9
1\leq a_i\leq 10^9
1≤ai≤109
题解:
考虑如果选择操作 1,一定是将最小的
a
i
a_i
ai 减
1
1
1 ,这样在之后进行操作 2 时,其他元素可以变得更小。因为操作 1 和操作 2 的次数是互相影响的,我们可以枚举其中一个操作的次数,然后计算出另一个操作的次数。
如果枚举操作 1 的次数,
1
≤
a
i
≤
1
0
9
1\leq a_i\leq 10^9
1≤ai≤109,必定超时。
考虑枚举操作 2 的次数,即枚举除了最小的元素,其余
n
−
1
n-1
n−1 个元素有多少个进行了操作 2 。设定有
m
m
m 个元素被赋值为最小元素,这些元素和最小的元素一起下降到
x
x
x ,使得最终有
x
×
(
m
+
1
)
+
未
被
操
作
元
素
≤
k
x\times (m+1)+未被操作元素 \leq k
x×(m+1)+未被操作元素≤k。
这里优先操作大的数,这样使得总和下降的更快。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200010;
int n;
ll a[N], k;
ll pre[N];
void solve() {
scanf("%d%lld", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
sort(a + 1, a + n + 1, greater<int>());
for (int i = 1; i <= n; ++i) pre[i] = pre[i - 1] + a[i];
if (pre[n] <= k) {
puts("0");
return ;
}
k = pre[n] - k;
ll ans = k;
for (int i = 1; i < n; ++i) {
// 这里先将 i 个元素修改成 a[n],然后这 i 个元素以及 a[n] 还需要如下公式的次数才可以不大于 k
ll y = max(0ll, k - pre[i] + a[n] * i + i) / (i + 1);
ans = min(ans, i + y);
}
printf("%lld\n", ans);
}
int main()
{
int T = 1;
scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
solve();
}
return 0;
}