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

题意

统计每个数的出现次数,设第一次取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;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/84437688
今日推荐