題目鏈接:
https://codeforces.com/problemset/problem/1622/C
題目大意:
給定一個序列 \(a_1\),\(a_2\),\(a_3\) ... \(a_n\),和一個 \(k\),一步操作中你可以選擇一個 \(a_i\) 減去 1,或者讓 \(a_i\) = \(a_j\),求最少多少步使 \(\sum_{i = 1}^n\)\(a_i\) <= k。
思路:
可以發現,整個操作的過程和序列的順序沒有關系,於是進行一個排序,使序列中的值從小到大。
容易想到 最優方案 一定是先進行 減 的操作再進行 賦值 的操作,即對最小的數進行一些減的操作,然后再通過最小數對其他數進行賦值賦值操作。
定義 \(i\) 為對 \(i\) 個數進行賦值操作,當賦值后值 不滿足 \(\sum_{i = 1}^n\)\(a_i\) <= k,要在賦值前進行 減 的操作,記 當前得到的總和為 \(p\),當前仍多出 \(p - k\),由於后續進行的是賦值的操作,那么 \(a_1\) 的變化也是后面 \(i\) 個數會發生的變化,那么 \(a_1\) 需要減去 \(\lceil(p - k) / (i + 1)\rceil\),即再進行 \(\lceil(p - k) / (i + 1)\rceil\) 次操作,而這個等式等價於 (p - k + i) / (i + 1),因為當 (p - k) % (i + 1) 有余數的時候,加上 \(i\),就實現了向上取整。
代碼:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
LL T, n, k;
void solve(){
cin >> n >> k;
vector <LL> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a.begin(), a.end());
vector <LL> sum(n + 1);
sum[0] = 0;
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
LL ans = 1e18;
for (LL i = 0; i < n; i++){
LL p = sum[n - i] + a[1] * i;
if(p > k) ans = min(ans, i + (p - k + i) / (i + 1));
else ans = min(ans, i);
}
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin >> T;
while(T--)
solve();
return 0;
}