解法一:前缀和+模拟
求增加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;
}