CF 1622C - Set or Decrease


題目鏈接:

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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM