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