题意
给出一个大小为 n 的数组,和一个整数 I ;
大小为 n 的数组,如果数组中不同数值的个数为K,则需要 nk 位来装这些数,
你可以进行一个操作,选择一个数值区间 [ L ,R ] 把大于 R 的数变成 R ,小于 L 的数变成 L ;给出的整数 I 代表字节数,一个字节8个位;问如果要使这个的数组可以被 I 字节装下,至少有几个数会被改变。
思路
通过给出了字节的大小,可以倒推到该字节数下装 n 个数,不同数值的最大值,即
K = 2 ^ [ (8.0 * I) / (1.0 * n) ]
因为如果要改变的数最小,必定不同数值的个数要尽可能的大。
因为改变数的操作是连续的一个区间,所以把数组从小到大排序;计算不同数值的数量,用 cnt 数组记录小于等于每种数值的个数总和,且升序排列(前缀和数组),最后遍历的 K 种连续的数值的数量总和取最大值,即取剩余数值数量总和的最小值。
代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int a[400100], c[400100];
int main(){
ll n, I;
scanf("%lld%lld", &n, &I);
for(int i = 0; i < n; i++){
scanf("%lld", &a[i]);
}
sort(a, a + n);
int K = 1, q = 1;
for(int i = 0; i < n; i++){
if(a[i] != a[i + 1]){
c[K] = c[K - 1] + q;
q = 1;
K++;
}
else{
q++;
}
}
int pK = (8.0 * I) / (1.0 * n);
if(pK > 19) pK = n;
else pK = 1 << pK;
if(pK >= K){
printf("0\n");
}
else{
int ans = 0x3f3f3f3f;
for(int i = 1; i + pK - 1 < K; i++){
int r = i + pK - 1;
ans = min(ans, n - c[r] + c[i - 1]);
}
printf("%d\n", ans);
}
}