题目链接:http://codeforces.com/contest/1077/problem/E
题意:首先给你一个长度为n的序列,表示一共有n个问题。其中某些问题属于同一种话题。现在要让你举办一天接一天的比赛,每天比赛要选择这些话题中的选择一个话题来进行解决问题。每天的比赛有如下要求:
第一 :当天话题对应的问题的个数应该是前天的两倍。如:前一天我们讨论的是话题3,问题个数为2的话,当天我们就得选择一个问题个数为4的话题。
第二:我们要求总共解决问题的个数最大。而不是举办比赛的次数最大。
第三:第一天我们可以选择任意数量的问题。
题解:我们可以知道第一天的选择问题的个数,直接导致了后面选择话题。我们可以暴力枚举第一天选择了几个问题。然后只需要从按照问题数量从小到大的排好序的话题中依次挑选合适的即可。
我们用二分的方法来判断当天需要问题个数的位置。
所以解决问题过程如下:
第一:对话题按照问题数量排序。
第二:枚举第一天选择了多少问题
第三:计算第一天问题选择的个数 i 能总共选出多少问题。
第四:求最大值。
注意:
第一:这个不能用桶排序的方法记录每个话题的数量。因为话题的大小为1e9。所以我们用map来存。然后再把结果放回数组中。
第二:当我们选择问题的数量大于2e5时,可以直接跳出选择话题的过程。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
struct node{
int key;
int val;
bool operator < (const node &a) const{
return val > a.val;
}
};
map<int,int> mp;
priority_queue<node> q;
int num[maxn];
int cnt ;
int slove(int x){
int count = x;
int flag = 1;
int pos = 0;
int sum = 0;
while(flag){
int s = lower_bound(num+pos,num+cnt,count) - num;
if(s < cnt){
pos = s + 1;
sum += count;
count <<= 1;
}
else flag = 0;
if(count > maxn) flag = 0;
}
return sum ;
}
int main(){
int n;
scanf("%d",&n);
for(int i = 0 ; i < n ; i ++){
int tmp;
scanf("%d",&tmp);
mp[tmp] ++;
}
map<int,int>::iterator it;
for(it = mp.begin() ; it != mp.end() ; it ++){
node now;
now.key = it->first;
now.val = it->second;
q.push(now);
//cout <<it->first << " " << it->second << endl;
}
cnt = 0;
while(!q.empty()){
node now = q.top();
num[cnt ++] = now.val;
q.pop();
}
int ans = -1;
for(int i = 0 ; i < maxn ; i ++){
// cout << ans << endl;
ans = max(ans,slove(i));
}
cout << ans << endl;
}