题意:给你n个数,一个大小为8*I的容量,保存一个数需要多少容量取决于给定n个数的种类k,用公式 log2 k 计算,如果给定的容量不能保存所有数,选择减少数的种类来降低保存一个数需要的单位容量(通过替换来减少数的种类,数据的总量不变),问最少需要替换多少个数
题解:根据输入数据的关系,可以求得保存一个数需要得最小单位容量kk=8*I / n;
又因为kk= log2 k 可以解得在容量允许情况下,可以保存得最多数据种类k=2kk
又因为输入数据个数得限制n<=4*10^5; 即2kk <=4*10^5;解得kk最小等于20;
所以当kk>=20得时候,一定可以把数据完全保存----------剪枝处理
当kk<20的时候:
由kk= log2 k 解得数据种类k=2^kk (快速幂)
因为要替换的个数最少,肯定是优先替换只出现过一次的数据,
滑动窗口处理[0,k-1]:
用一个大小为k的窗口去判断在窗口内的要替换的最小个数
#include<iostream> #include<algorithm> #include<math.h> #include<string> #include<string.h> #include<vector> #include<utility> #include<map> #include<queue> #include<set> #define mx 0x3f3f3f3f #define ll long long using namespace std; map<ll,ll>m; vector<ll>p; ll a[400005]; ll quick_pow(ll base,ll k) { ll ans=1; while(k) { if(k&1) ans=ans*base; base=base*base; k=k/2; } return ans; } int main() { ll n,k,cap;//容量capacity scanf("%lld%lld",&n,&cap); for(int i=0;i<n;i++) scanf("%lld",&a[i]); sort(a,a+n); cap=cap*8;//给定的容量有多大 int kk=cap/n;//由输入的n和cap可以估计每存一个数需要的最少容量是多少 if(kk>=20)//剪枝处理,如果log以2为底kk的对数等于20,解得数字的种类k=2^20,远大于给定输入数据的个数4*10^5 { printf("0\n"); return 0; } k=quick_pow(2,kk);//由估计的容量kk去解最多可以容纳几种不同的数 m[a[0]]++; p.push_back(a[0]); for(int i=1;i<n;i++) { if(a[i]!=a[i+1]) p.push_back(a[i]); m[a[i]]++; } ll cnt=p.size(); if(cnt<=k)//给定得容量能容纳这些数,就不用减少种类 { printf("0\n"); return 0; } else { ll l=0,r=k-1,sum=0; for(int i=l;i<=r;i++) sum=sum+m[p[i]];//统计重复的数有几个 ll one=n-sum;//只出现一次的数的个数 l++;//滑动窗口,这个窗口的大小是k,[0,k-1] r++; while(r<cnt) { sum=sum-m[p[l-1]]+m[p[r]]; ll temp=n-sum; one=min(one,temp); l++; r++; } printf("%lld\n",one); } }