传送门:https://codeforces.com/contest/1490/problem/F
题意:
给一个长度为n的数组a
要使a中所有元素的出现次数一样
求最少要移除元素的个数
思路:
英文题面的题意总是理解错误,哭了/(ㄒoㄒ)/~~
要求删除多少个元素,我们可以知道,删除元素肯定是和元素出现次数相关的
先把数组a转化成元素出现次数的数组b并从小到大排序,如:
6
1 3 2 1 4 2
应该转化成:
4 //cnt
1 1 2 2 //3和4出现一次,1和2各出现两次
那我们只需要遍历b中不同的出现次数
求该出现次数下至少要删除多少个数才能让所有数的出现次数一样,
即通过减少b[i]让b中的所有数值相同,
接下来分析如何计算删除次数,sum[i]表示b数组的前i个数的前缀和
假设在b数组中对于区间[l,r],
b
[
l
]
=
b
[
l
+
1
]
=
.
.
.
=
b
[
r
]
(
l
<
=
r
)
b[l]=b[l+1]=...=b[r](l<=r)
b[l]=b[l+1]=...=b[r](l<=r)
- 对于
b
[
i
]
(
i
<
l
)
b[i](i<l)
b[i](i<l)都有
b
[
i
]
<
b
[
l
]
b[i]<b[l]
b[i]<b[l],小于b[l]的数无法减少到和b[l]一样,只能全部删除
提供贡献 s u m [ l − 1 ] sum[l-1] sum[l−1] - 对于
b
[
i
]
(
i
>
r
)
b[i](i>r)
b[i](i>r)都有
b
[
i
]
>
b
[
r
]
b[i]>b[r]
b[i]>b[r],大于b[r]的数全部减小至和b[r]一样
提供贡献 s u m [ c n t ] − s u m [ r ] − ( c n t − r ) ∗ b [ r ] sum[cnt]-sum[r]-(cnt-r)*b[r] sum[cnt]−sum[r]−(cnt−r)∗b[r]
综上: a n s = m i n { s u m [ l − 1 ] + s u m [ c n t ] − s u m [ r ] − ( c n t − r ) ∗ b [ r ] } ans=min\{sum[l-1]+sum[cnt]-sum[r]-(cnt-r)*b[r]\} ans=min{sum[l−1]+sum[cnt]−sum[r]−(cnt−r)∗b[r]}
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f,maxn=2e5+10;
ll t,n,cnt,a[maxn],b[maxn],sum[maxn];
map<int,int> mp;
int main() {
cin>>t;
while(t--) {
mp.clear();
memset(sum,0,sizeof sum);
memset(b,0,sizeof b);
cin>>n;
for(int i=1; i<=n; i++) cin>>a[i];
sort(a+1,a+1+n);
cnt=1;
for(int i=1; i<=n; i++) { //将a转化为不同元素出现次数的数组b
if(!mp[a[i]]) mp[a[i]]=1,b[++cnt]++;
else b[cnt]++;
}
sort(b+1,b+1+cnt);
for(int i=1; i<=cnt; i++) sum[i]=sum[i-1]+b[i];
ll l=0,r=0,ans=inf;
while(r+1<=cnt) {
l=++r;
while(b[r]==b[r+1]) r++;
ans=min(ans,sum[l-1]+sum[cnt]-sum[r]-(cnt-r)*b[r]);
}
printf("%lld\n",ans);
}
}