当前位置: 首页 > 工具软件 > Thematic > 使用案例 >

【 Codeforces Round #521 (Div. 3) E. Thematic Contests】二分+STL

淳于坚壁
2023-12-01

E. Thematic Contests

题意
现在有n个题目,每种题目有自己的类型, 1 &lt; = n &lt; = 2 ∗ 1 0 5 1&lt;=n&lt;=2*10^{5} 1<=n<=2105
要举办一次考试,
考试的原则是
每天只有一种题目类型
一种题目类型只能出现在一天
每天的题目类型不能相同,
而且后一天的题目数量必须正好为前一天的两倍,第一天的题目数量是任意的
求怎么安排能使题目尽量多。注意:不是天数尽量多

做法
首先我们知道一件事,题目属于哪种类型并不重要,重要的是每种类型的个数
所以我们先统计出所有类型的个数,存进一个数组,
而最终最优解一定是按照题目数量从小到大来的,所以我们对该数组进行排序
而我们怎么确定最终怎么选择呢,我们发现最终怎么选择取决于第一天怎么选择
而且只要确定了第一天的题目数,只要在排序好的数组上采取能选就选的原则,
就可以 O ( n 2 ) O(n^2) O(n2)的时间复杂度解决这道题
可是这个复杂度并不能通过,所以我们要看哪个过程可以优化
第一天的题目数并不满足二分的性质,我们就考虑优化能选就选这个原则
在排好序的数组上我们可以通过lower_bound快速找到>=当前所需数目的下标pos
之后让pos++,继续下一次查找,这样我们最多查找log次就能完成check
于是这道题就是 O ( n ) O(n) O(n)枚举第一天的题目数, O ( l o g n ) O(logn) O(logn)check,总复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

坑点

c h e c k 的 时 候 所 需 题 目 数 &gt; = 2 ∗ 1 0 5 check的时候所需题目数&gt;=2*10^5 check>=2105直接跳出
每次找到下标之后让下标++,并在之后的范围内二分

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
int a[maxn];
map<int,int> mp;
int x[maxn];
int n,cnt;
int solve(int mid)
{
    int tmp=mid;
    int ans=0;
    int pos=1;
    while(true)
    {
        pos=lower_bound(x+pos,x+1+cnt,tmp)-x;
        if(pos==cnt+1) break;//没找到直接break
        ans+=tmp;
        tmp=tmp*2;//下一天需要的题目数翻倍
        pos++;
        if(pos==cnt+1||tmp>200000) break;//找到头或者不可能继续找
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        mp[a[i]]++;
    }
    map<int,int>::iterator it;
    for(it=mp.begin();it!=mp.end();++it)
    {
        x[++cnt]=(*it).second;
    }
    sort(x+1,x+1+cnt);//得到每种类型的数目,并排序
    int ans=0;
    for(int i=1;i<=200000;i++) ans=max(ans,solve(i));//O(n)枚举第一天的题目数
    printf("%d\n",ans);
    return 0;
}


 类似资料: