参考题解:传送门
题意:一组共有奇数个元素的数列,可以进行k次+1操作(在数列中任意选择数字)问可以生成的最大中位数是多少。
这道题后来看题解看了很久才想明白二分是在干什么。
其实,二分是在暴力的查询所有可能的中位数值,再验证能否在k次内生成。所有可能的中位数数值,最小的必然是原数列不经任何操作的中位数数值,最大的则要根据题目给出的数据范围确定,k最大1e9,数字最大1e9,所以最极端的情况就是这1e9次加法全都加给中位数,那么最大就是2e9(不过这种情况显然是不成立的,因为k全部用尽,这必然不是中位数了)。
所以标程里的一坨二分干的就是这个工作,暴力的找。找完了就要查验了,查验的方法其实也是在暴力。
因为我们要确保这个数是中位数,那么如果它自身进行++,可能会超过后面的数字,一旦超过,就需要给后面的数也++,至少让它们持平,才可以保证我们暴力生成的这些数依然是符合要求的中位数。所以,从中位数的位置往后扫到最后一个,如果比中位数小,就++直到持平。这里面可以使用的总次数就是k。
注意:我们这个for循环是从下标mid开始,因为步骤一里面二分生成中位数也是要消耗k的,我们在上面没有算,在这里一起算。
最后的最后……别忘了sort啊……脑内sort没用的
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAX_N 200020
#define ll long long
ll n, k, ans, mid;//n个数字(奇数个)的数列 最多进行k次+1操作
ll num[MAX_N];
bool check(ll m)
{
if(m <= num[mid])
return 1;
ll cnt = 0;
for(int i = mid;i <= n;i++)
{
if(num[i] < m)
cnt += m - num[i];
if(cnt > k)
return 0;
}
return 1;
}
int main()
{
scanf("%lld %lld", &n, &k);
for(int i = 1;i <= n;i++)
scanf("%lld", &num[i]);
sort(num+1, num+n+1);
mid = (n+1)/2;//存储当前的中位数
ll l = num[mid], r = 2e9+1;
//下面要枚举所有可能的中位数取值
while(l <= r)
{
ll m = (l+r)/2;
if(check(m))
{
l = m+1;
ans = m;
}
else
r = m-1;
}
printf("%lld\n", ans);
return 0;
}