题意
城里有 n 户人家,每户人家有 mi 个金币。robin 每天从最富的人家偷一个金币送给最穷的一户人家。问 k 天后最富的一户人家比最穷的一户人家多多少金币。
思路
首先要想明白一个问题,金币只是进行交换用的,所以金币的总数是不变的,然后我们考虑一下最终的状态,最富的人一定是一个固定的金币数,最穷的呢,也是一个固定的金币数,那么我们把从最开始最富的人的金币数记录下来,一直到最后,我们发现这个曲线一定是一个递减的,同样最穷的人的金币一定是递增的,并且最重要的一点是最富的一定大于等于金币的平均值,最穷的呢一定小于等于平均值,这样最富最穷的范围就出来了,然而它们的变化趋势也有了,这样我们就可以根据递增递减这个性质来进行二分查找了,分别二分出最富和最穷然后做一下差就得出答案了; 注意中间变量爆int;
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x7f7f7f7f
const int maxed = 500000 + 10;
typedef long long ll;
ll n, m, p[maxed];
int main()
{
scanf("%lld%lld", &n, &m);
ll max_ = 0, sum = 0, l = INF;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &p[i]);
max_ = max(max_, p[i]);
sum += p[i];
l = min(l, p[i]);
}
ll min_ = sum/n, r = max_;
if (sum % n)
min_ += 1;
//cout << sum << "---" << n << endl;
while (max_ >= min_) {
ll mid = (max_ + min_) >> 1, sum_ = 0;
for (int i = 1; i <= n; ++i)
sum_ += max(0LL, p[i] - mid);
if (sum_ <= m) {
r = min(r, mid);
max_ = mid - 1;
}
else
min_ = mid + 1;
}
min_ = l; max_ = sum/n;
while (max_ >= min_) {
ll mid = (max_ + min_) >> 1, sum_ = 0;
for (int i = 1; i <= n; ++i)
sum_ += max(0LL, mid - p[i]);
if (sum_ <= m) {
min_ = mid + 1;
l = max(l, mid);
}
else
max_ = mid - 1;
}
//cout << r << "-----" << l << endl;
printf("%lld\n", r - l);
return 0;
}