解法一:前綴和+模擬
求增加k之後的最大中位數,必定是在後半段區間進行操作。我們可以很容易想到,先將中間數增大,然後再對後半段區間進行操作使中間數還是中位數,而使an/2還是中位數的話,後面的數仍是升序。
a(n/2)增加到a(n/2)+1,總共需要加(a(n/2)+1-a(n/2)) 1
a(n/2)增加到a(n/2)+2,總共需要加(a(n/2)+1-a(n/2)) 1 + (a(n/2)+2-a(n/2)+1) 2
a(n/2)增加到a(n/2)+3,(a(n/2)+1-a(n/2)) 1 + (a(n/2)+2-a(n/2)+1) 2 + (a(n/2)+3-a(n/2)+2 3
…
依次類推,這就是一個前綴和表達式。因此我們只需要找到k所處的範圍,並進行不能完全增到到a(n/2)+?的處理。時間複雜度O(n)
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
const int MAXN = 2e5 + 10;
long long a[MAXN], b[MAXN];
using namespace std;
typedef long long ll;
int n,k;
int main()
{
scanf("%d%d", &n, &k);
_for (i, 0, n) scanf("%d", &a[i]);
sort(a, a+n);
int cnt = 0;
_for (i, n/2+1, n) b[++cnt] = a[i] - a[i - 1];//計算前綴和
int ans = 0;
//開始模擬
_rep (i, 1, cnt)
if (k >= ll(b[i] * i)) {k -= b[i]*i; ans += b[i];}//如果k滿足增加到a[(n/2)+i]所需要的花費,那麼中間數則可以增加b[i]
else {ans += k / i; k = 0;break;}//不能滿足增加到a[(n/2)+i]所需要的花費,從最右端開始一個一個增加,中間數只能加最少的
if (k)//如果處理完,k還有剩餘。那麼說明中間數已經增加到a[n-1]
{
ans += k / (n/2 + 1);
}
printf("%d\n", a[n/2] + ans);
}
解法二:二分答案
二分查找最大中位數,模擬增加量與k的關係,判斷是否最大。
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
using namespace std;
typedef long long ll;
ll n, k;
ll x;
vector<ll> a;
//模擬x爲中位數需要增加多少moves
bool check(ll x)
{
ll moves = 0;
_for (i, n / 2, n)
{
if (x - a[i] > 0) moves += x - a[i];
if (moves > k) return false;
}
if (moves <= k) return true;
else return false;
}
int main()
{
ios_base::sync_with_stdio(false);
cin >> n >> k;
_for (i, 0, n) {cin >> x; a.push_back(x);}
sort(a.begin(), a.end());
ll l = 1, r = 2e10, mid;
//查找滿足條件的最大值
while (l != r)
{
mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l;
}