1. 程式人生 > >CodeForces - 1077(div3) E.Thematic Contests(列舉+二分)

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;
}