牛客小白月赛80

 C/D题详细题解    二分答案

一共有m个班级,因为会有拖堂的班级,所以每一个班级都需要判断,有m种情况:

1、首先可以先判断,总人数 - 当前拖堂的人数 <  离校人数  ( n - a[i]  < k) 

这里a[i]表示第i个班级的人数 ,如果小于k ,说明剩余的人达不到题目要求输出-1;

2、接下来考虑用二分答案,二分的mid表示还留在学校最多人数的班级是mid,所以不能有超过mid的人数的班级存在,所以判断超过了mid的班级减去mid之后的超过人数总和能否等于k, 也就是 求出 所有班级人数 >= mid 的班级超过mid的部分的总和 跟k进行比较

3、只需要求出大于mid的班级人数之和  、 大于mid的班级数量,维护两个前缀和就可以实现,详细看代码

import java.util.*;
import Hosted by one.com.*;
public class Main{
    static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter wt = new PrintWriter(new OutputStreamWriter(System.out));
    static StreamTokenizer sv = new StreamTokenizer(bf);
    static int N = 1000010;
    static int[] a = new int[N];     //a[i] 表示 班级 i 的学生人数
    static int[] sum1 = new int[N];  //sum1[i]表示班级学生人数 >= i 的班级总数量
    static int[] sum2 = new int[N];  //sum2[i]表示班级学生人数 >= i 的学生总数量
    static int n, m, k;
    
    //二分答案
    //函数枚举的是答案,也就是mid是还留在学校的最多的班级数量
    //返回一个那些超过了mid的班级减去mid之后的总和能否等于k
    public static int check(int x, int i) {
              // 班级人数大于等于x的总人数 - 班级人数大于等于x的班级数量 * 当前二分的答案x,就能得出 离开学校的人的数量 然后跟k进行比较   
              // 因为当前i是拖堂的班级, 所以需要判断是不是也在 >= x的范围里面,然后减去对应的班级人数 - a[i] 和 班级数量 - 1
        return (sum2[x] - (a[i] >= x ? 1 : 0) * a[i]) - (sum1[x] - (a[i] >= x ? 1 : 0)) * x; 
    }
    
    public static void main(String[] args)throws Exception{
        n = nextInt();
        m = nextInt();
        k = nextInt();
        
        for (int i = 0; i < n; i ++ ) {
            int x = nextInt();
            a[x]++;          //统计每一个班级的学生数量
        }
        
        
        //a[i] 表示当前第i个班级的学生人数  
        for (int i = 1; i <= m; i ++ ) {    //统计每一个班级
            sum1[a[i]] ++;         //统计当前这种学生人数的班级数量     
                                   //例如;a[0] = 3  a[1] = 2  a[2] = 3   
                                   //  sum1[3] = 2   数量为3的班级数量有两个 
                                   //  sum1[2] = 1    数量为2的班级数量有一个
            
            sum2[a[i]] += a[i];    // 统计当前这种学生人数的学生人数
                                   //例如;a[0] = 3  a[1] = 2  a[2] = 3  
                                   // sum2[3] = 6   数量为3的班级人数有6人
                                   // sum2[2] = 2   数量为2的班级人数有3人
        }
        
        //求前缀和   因为求的是 >= 某个数量的总人数 或者 总数量
        //所以倒着求前缀和,就可以统计到先统计到的比当前数大的数据
        for (int i = n - 1; i >= 0; i --) {
            sum1[i] += sum1[i + 1];  //sum1[i]表示班级学生人数 >= i 的班级总数量
            sum2[i] += sum2[i + 1];  //sum2[i]表示班级学生人数 >= i 的学生总数量  
        }
        
        for (int i = 1; i <= m; i ++ ) {      
            if (n - a[i] < k) wt.print(-1 + " ");   // 如果剩下的人不够离校k人,输出 -1
            else {
                int l = 0, r = n;      
                while (l < r) {
                    int mid = l + r >> 1;
                    if (check(mid, i) <= k) r = mid;    //二分  如果返回的可离校的人数 <= 指定离校数量k 
                    else l = mid + 1;          //满足离校人数 >= 指定离校数量k
                }
                wt.print(l + " " );
            }
        }
        wt.flush();
    }
    
    public static int nextInt() throws Exception {//int型
        sv.nextToken();
        return (int) sv.nval;
    }
    public static long nextLong() throws Exception {//long型
        sv.nextToken();
        return (long) sv.nval;
    }
    public static String next() throws Exception {
        return bf.readLine();
    }
    public static String[] nextarr() throws Exception {
        return bf.readLine().split(" ");
    }
    public static int stoi(String s) throws Exception {
        return Integer.parseInt(s);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栗子ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值