codeforces 671B Robin Hood 二分

题意:有n个人,每个人a[i]个物品,进行k次操作,每次都从最富有的人手里拿走一个物品给最穷的人

        问k次操作以后,物品最多的人和物品最少的人相差几个物品

分析:如果次数足够多的话,最后的肯定在平均值上下,小的最多被补到sum/n,大最多减少到sum/n,或者sum/n+1

        然后就二分最小值,看所有小的是否能在k次被填满

                二分最大值,看所有大的是否都在k次被抹平

然后就是二分的写法,小和大的不一样,需要加等号,避免死循环

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long LL;
const int N = 5e5+5;
int a[N],k,n;
bool checkmax(int x){
   int tmp=k;
   for(int i=1;i<=n;++i){
    if(a[i]>x)tmp-=(a[i]-x);
    if(tmp<0)return false;
   }
   return true;
}
bool checkmin(int x){
  int tmp=k;
  for(int i=1;i<=n;++i){
    if(a[i]<x)tmp-=(x-a[i]);
    if(tmp<0)return false;
  }
  return true;
}
int main(){
    scanf("%d%d",&n,&k);
    LL sum=0;
    int k1,k2,x=0,y=0x7f7f7f7f;
    for(int i=1;i<=n;++i){
      scanf("%d",&a[i]);
      sum+=a[i];
      x=max(x,a[i]);
      y=min(y,a[i]);
    }
    k1=k2=sum/n;
    if(sum%n)++k2;
    int ans2,ans1,l=y,r=k1;
    while(l<=r){
       int m=(l+r)>>1;
       if(checkmin(m))ans1=m,l=m+1;
       else r=m-1; 
    }
    l=k2,r=x;
    while(l<r){
        int m=(l+r)>>1;
        if(checkmax(m))r=m;
        else l=m+1;
    }
    ans2=(l+r)>>1;
    printf("%d
",ans2-ans1);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/shuguangzw/p/5485473.html