2019沈阳网络赛(F)Honk's pool二分

As we all know, Honk has n pools, numbered as 1 ~ n . There is ai liters water in the i-th pool. Every day, Honk will perform the following operations in sequence.

  1. Find the pool with the most water (If there are more than one, choose one at random) and take one liter of water.
  2. Find the pool with the least water (If there are more than one, choose one at random) and pour one liter of water into the pool.
  3. Go home and rest (Waiting for the next day).

Please calculate the difference between the amount of water in the pool with the most water and the amount of water in the pool with the least water after the k days.

Input

The input consists of multiple test cases. The input is terminated by the end of file.The number of data sets will not exceed 40

The first line of each test case contains two integers n and k, which indicate the number of pools and the number of days to operate the pool.

The second line of each test case contains n integers, and the ii-th number represent ai indicating the initial amount of water in the i-th pool.

1≤n≤500000, 1≤k≤10^9, 1≤ai≤10^9.

Output

For each test case, print one line containing the answer described above.

样例输入

4 100
1 1 10 10

样例输出

1

样例输入

4 3
2 2 2 2

样例输出

0
链接:

https://nanti.jisuanke.com/t/41406

题意:

给你一个n数组,每次挑出最大的,令其减一,然后挑出最小的,令其加一。共操作 K 次,求最大值和最小值的差。

思路:
方案一:直接暴力模拟

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
multiset<ll>ste;
int main()
{
    int n;ll k;
    while(~scanf("%d%lld",&n,&k))
    {
        ste.clear();
        for(int i=1;i<=n;++i)
        {
            ll a;
            scanf("%lld",&a);
            ste.insert(a);
        }
        multiset<ll>::iterator it1,it2;
        while(k--)
        {
            it1=ste.begin();
            ll a=*it1;
            ste.erase(it1);
            ste.insert(++a);

            it2=--ste.end();
            a=*it2;
            ste.erase(it2);
            ste.insert(--a);
        }
        ll ans=(*(--ste.end()))-(*ste.begin());
        printf("%lld\n",ans);
    }
    return 0;
}
方案二:二分

假设k特别大,那么最终的结果应该接近平均数,所以我们先求出平均数,然后分成两部分,左部分比平均数小,右部分比平均数大。然后分别对两部分进行二分,对右部分二分结果表示k次操作内可以使右部分内的最大值变到最接近平均数的值。对左部分二分结果表示k次操作内可以使左部分的最小值变到最接近平均数的值。然后我们可以右部分的结果-左部分的结果就是我们要求的结果。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[500000+50];
int n, k;
bool check(int mid, bool flag)
{
    ll sum = 0;
    for(int i = 1; i <= n; i++) {
        if(flag && a[i] > mid)
            sum += a[i] - mid;
        else if(!flag && a[i] < mid)
            sum += mid - a[i];
    }
    if(k >= sum) {
        return true;
    }
    else
        return false;
}
int main()
{
    while(~scanf("%d%d", &n, &k)) {
        ll sum = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            sum += (ll)a[i];
        }
        int ave;
        if(sum % n == 0)
            ave = sum / n;
        else
            ave = sum / n + 1;
        sort(a+1, a+n+1);
        int l = ave, r = a[n];
        while(l < r) {
            int mid = l + r >> 1;
            if(check(mid, true)) {
                r = mid;
            } else
                l = mid + 1;
        }
        int ans1 = r;
        l = a[1], r = ave;
        if(sum % n)
            r--;
        while(l < r) {
            int mid = l + r + 1 >> 1;
            if(check(mid, false)) {
                l = mid;
            } else
                r = mid - 1;
        }
        int ans2 = l;
        cout << ans1 - ans2 << endl;
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值