题意
统计每个数的出现次数,设第一次取a个数i,
则第二次必须取2*a个数j,第三次4*a个数k,以此类推
最大化取出的数的个数总和,即a+2*a+4*a+…。
思路来源
https://blog.csdn.net/nucleare/article/details/84191237
题解
枚举第一个取的值a,在有序序列[l,r]里不断二分2*a,
找到后就在j处取2*a,
再令l=j+1,在[l,r]内二分4*a,
将区间缩小,复杂度严格O(nlogn)。
心得
①对于统计次数的离散化操作,可以令其映射数组单增标号。
这样就不用自己开vector塞入出现的数的操作了。
②在有序序列里不断二分的操作,也是之前没有遇到过的。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const int INF=0x3f3f3f3f;
const int maxn=2e5+10;
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pli pair<ll,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int n,ans[maxn];
ll res;
map<int,bool>exist;
map<int,int>num;
vector<int>q;
int main()
{
sci(n);
rep(i,0,n-1)
{
int x;
sci(x);
if(!exist[x])q.push_back(x);
exist[x]=1;
num[x]++;
}
int len=q.size();
rep(i,0,len-1)q[i]=num[q[i]];
sort(q.begin(),q.end());
rep(i,1,q[len-1])
{
ll tmp=0,now=i;
int j=0;
while(j<len)
{
j=lower_bound(q.begin()+j,q.end(),now)-q.begin();
if(j==len||q[j++]<now)break;//防止越界的同时,将二分区间缩小到[j+1,len-1]
tmp+=now;now*=2;
}
res=max(res,tmp);
}
printf("%lld\n",res);
return 0;
}