nyoj 2354 同時也是 codeforces Robin Hood(二分)

ACM_e發表於2017-09-02

2354: 分班級

時間限制: 1 Sec  記憶體限制: 128 MB
提交: 121  解決: 10
[提交][狀態][討論版]

題目描述

        Zoro是一個有強迫症的人,他喜歡均衡。
17級的新生要開學了,起初所有班級是按照學生的來源地分的,各班人數非常不合理。於是老師要求Zoro來讓各班人數均衡一下。
        由於學校系統陳舊,每次調換隻能是Zoro自己手動的把一個學生從一個班級拉到另外一個班級,由於Zoro有強迫症,他每次會找出班級人數最多的那個拉出一個人轉到一個人數最少的班級,而且每次操作,耗費一次許可權,而他的賬號只有k次許可權。老師最後會看所有班級人數差的最大值。
由於班級實在太多,Zoro要做完需要很長時間,你能不能幫助Zoro先計算出他工作完成後所有班級人數差的最大值報告給老師。

輸入

第一行輸入兩個整數n和k,分別代表班級數量和Zoro賬號的操作許可權次數。
接下來一行n個整數 第i個數字代表第i個班級有ci個人。
(1<=n<=500000,0<=k<=1e9,0<= ci <=1e9)

輸出

輸出一個整數表示最後所有班級人數差的最大值。

樣例輸入

5 1
1 2 3 4 5

樣例輸出

2


思路:


1、一直在想二分差值,然後發現最小值和最大值的問題不是很好處理,一直在想一個科學的連續二分的方式去列舉出最小值和最大值。以一種二分套二分的方式去解,以失敗告終。


2、正解是這樣的:

首先得明確一點,初始時硬幣數比平均數少的人最終會增加k個硬幣,比平均數多的會減少k個硬幣,
所以根據這個先用二分求出最終最少的硬幣數,然後最用二分求出最終最多的硬幣數,然後相減就行了,明確這一點這題目差不多就出來了。。。

①因為時間越長(天數經過的越多),最大值就會越小,同理,最小值就會越大(所以這就是智障的去二分差值的理由?尼瑪本質是最大值最小值的變化好嘛,為毛要想到差值上去.......)那麼我們二分一個最小值,接下來二分一個最大值。

②判定二分的過程很簡單,對於列舉最小值的時候,如果需要的最少天數小於等於k,那麼就加大最小值,否則減小最小值即可。那麼二分列舉最大值的時候同理即可。

③問題的坑點在於維護上下界。對於總值:sum,如果sum%n==0,那麼最小值的上界就是sum/n,最大值的下界也是sum/n.當sum%n!=0的時候,最小值的上界還是sum/n,但是最大值的下界應該是sum/n+1.這裡被坑了......................


這個從最大的拿東西給最小的,不是列舉差值。。






#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 5e5 + 5;
typedef long long ll;
ll a[maxn], n, k;
int check(ll x)   //判斷以這個x為最小的天數,需要多少天
{
    ll res = 0;
    for(int i = 1; i <= n; i++)
    {
        if(a[i] < x) res += x-a[i]; //比他小, 就說明這些都要得到1,最終達到最小的天
    }
    return res <= k;
}
int check2(ll x)
{
    ll res = 0;
    for(int i = 1; i <= n; i++)
    {
        if(a[i] > x) res += a[i]-x;  //比他大, 就往外給,最終到達最大的天
    }
    return res <= k;
}
int main()
{
    scanf("%lld%lld", &n, &k);
    ll sum = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        sum += a[i];
    }
    ll limit = sum / n;
    ll limit2 = limit;
    if(sum%n) limit2++;                   //
    ll ansx = 0, ansy = 0;
    ll l = 1, r = limit, mid;             //mid  表示 最大或者最小能夠到達的值
    while(l <= r)
    {
        mid = (l+r)/2;                    
        if(check(mid))              
        {
            ansx = mid;
            l = mid + 1;
        }
        else
            r = mid - 1;
    }
    l = limit2;
    r = sum;
    while(l <= r)
    {
        mid = (l+r)/2;
        if(check2(mid))
        {
            ansy = mid;
            r = mid - 1;
        }
        else
            l = mid + 1;
    }
    printf("%lld\n", ansy-ansx);
    return 0;
}


相關文章