CodeForces - 1077(div3) E.Thematic Contests(列舉+二分)
阿新 • • 發佈:2018-12-03
題意
統計每個數的出現次數,設第一次取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; }