蓝桥杯 java 最少刷题数


import java.io.*;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: MR
 * @Date: 2024/3/2 15:39
 * @Description :
 * 小蓝老师教的编程课有 N 名学生,编号依次是 1...N。第i号学生这学期刷题的数量是 4:。
 * 对于每一名学生,请你计算他至少还要再刷多少道题,才能使得全班刷题 比他多的学生数不超过刷题比他少的学生数。
 * <p>
 * <p>
 * 数据范围: 1<= N <= 1e5     0<= Ai <= 1e5
 * <p>
 * 思路:刷题比它多的不超过比他少的 也就是 多的<=少的
 * 数据范围都是不超过1e5
 * <p>
 * 我们有个需求:我们要找到比他刷题多的学生 和 比他刷题少的学生的人数
 * 为什么使用前缀和?就是使用一个区间来统计刷题人数
 * 我们就使用前缀和
 * 比如我们开个1e5的数组 我们用下标来统计题数 元素统计这个题完成的人数
 * 我们就可以快速求得某个区间中的刷题人数
 * 比如这个题是x  s[x-1]  s[x]  s[N]-s[x]
 * 比他少    一样   比他多(全部的题-和他一样的 剩下的就是比他多的)
 * 题目的要求是比他多的学生不超过比他少的 多<=少 s[N]-s[x] <= s[x-1]
 * <p>
 * 为什么使用二分:
 * 随着刷题量的增加 刷题比我多的人会变少 比我少的人会变多
 * 当前刷了t道题符合条件 那是不是t+1 t+2 这边的区间 都满足条件 反之另一边一定不符合 这就是二段性 所以我们使用到了二分
 * 我们只需要找最低的那个t满足要求的临界点
 */
public class 最少刷题数 {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static int N = 100010;
    static int[] a = new int[N]; // 表示题数
    static int[] s = new int[N]; // 前缀和 数组用来存某个区间的刷题人数

    public static void main(String[] args) throws IOException {
        // 人数
        int n = Integer.parseInt(br.readLine());
        // 使用数组来存每个学生的刷题数
        String[] str = br.readLine().split(" ");
        for (int i = 0; i < n; i++) {
            // 让a来存录入的学生的刷的题数
            a[i] = Integer.parseInt(str[i]);
            s[a[i]]++; // 比如a[i]=12 那么s[12]++  让数组下标s[12]的元素+1 代表刷了12题的学生多一个
        }
        // 变成前缀和数组 方便我们后续统计某个区间里刷题有X个学生
        for (int i = 1; i <= 100000; i++) {
            s[i] += s[i - 1];
        }

        // 判断当前题量的学生是否需要加题
        for (int i = 0; i < n; i++) {
            // 翻译: s[N] -s[x] <= s[x-1]
            //  刷题比我多的<=刷题比我少的 说明不需要加题  这里取一个max 是因为刷题可以为0  s[i]-1 可能越界 所以需要和0比个max
            if (s[100000] - s[a[i]] <= s[Math.max(0, a[i] - 1)]) {
                out.print(0 + " ");
                continue;
            }
            //  二分找应该刷多少题才合适
            // l下限最少多一题 r上限1e5
            int l = a[i] + 1, r = 100000;
            while (l < r) {
                int mid = l + r >>> 1;      // 这里-1是去掉自己 如果我90分 我到100分了 90分的人就少了一个
                if (s[100000] - s[mid] <= s[mid - 1] - 1) r = mid;
                else l = mid + 1;
            }
            // r是我需要刷的题 - 我已经刷了的题 剩下的就是我需要多刷的题
            out.print(r-a[i]+ " ");
        }
        out.flush();
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值