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 
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 
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;
}

你可能感兴趣的:(二分)