CodeForces - 1077(div3) E.Thematic Contests(枚举+二分)

题意

统计每个数的出现次数,设第一次取a个数i,

则第二次必须取2*a个数j,第三次4*a个数k,以此类推

最大化取出的数的个数总和,即a+2*a+4*a+…。

思路来源

https://blog.youkuaiyun.com/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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值