网址:http://codeforces.com/contest/1077/problem/D
题面的意思是让你在长度n的数组中选出k个可以重复的数,这k个可以重复的数的组合在原数组中出现的次数t最大,输出选择方案。题面比较绕,一定要好好看举的例子。
可以发现,对于所有数字按照出现频率排序以后,优先选择出现次数多的数是肯定没有问题的。但是有些出现次数多的数可以多次选择,这个题就变的比较麻烦。我们发现,如果对于给定的t,可以在O(n)时间算出答案,并且t具有单调性(如果一个数组可以重复5次,就一定可以重复4次3次2次。一个数组不能重复5次,就肯定不能重复6次7次8次)。所以我们可以对t进行二分。这样这道题就结束了。
#include <cstdio>
#include <map>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=200005;
struct Num{
int time,val;
}nums[N];
int k,n,a[N],cnt=0,b[N],ans[N],cnt2=0;
map<int,int> m;
bool operator<(Num a,Num b) {
return a.time>b.time;
}
int main(void) {
scanf("%d%d",&n,&k);
for (int i=1;i<=n;++i) {
scanf("%d",a+i);
if (m.count(a[i]))
++m[a[i]];
else
m[a[i]]=1;
}
for (auto it=m.begin();it!=m.end();++it) {
nums[cnt].time=it->second;
nums[cnt++].val=it->first;
}
sort(nums,nums+cnt);
int l=1,r=n,t=0;
while (l<=r) {
t=(l+r)>>1;
cnt2=0;
for (int i=0;i<cnt;++i) {
for (int j=0;j<nums[i].time/t;++j)
b[cnt2++]=nums[i].val;
if (cnt2>=k)
break;
}
if (cnt2>=k) {
l=t+1;
memcpy(ans,b,k*sizeof(int));
} else
r=t-1;
}
for (int i=0;i<k;++i)
printf((i==k-1)?"%d\n":"%d ",ans[i]);
return 0;
}