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

来源:互联网 发布:智能优化算法的优缺点 编辑:程序博客网 时间:2024/05/16 10:51

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


原创粉丝点击