codeforces-1198A MP3

codeforces-1198A

题意

给出一个大小为 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 
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);
	}
}

你可能感兴趣的:(codeforces-1198A MP3)