1 条题解

  • 0
    @ 2025-8-24 22:04:23

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 叶小枫
    爱能做到的,还有很多啊

    搬运于2025-08-24 22:04:23,当前版本为作者最后更新于2018-09-18 23:04:36,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    一道卡桶排卡的很死的题目


    基本思路

    首先我们拿到这道题,先来读一遍……读完之后发现这道题好像闹着玩一样,就是每次把最小的那个数乘2就好了。只是有一点需要注意那就是最后一次比较的判断:如果一趟乘2下来后第二大的数字乘上2比最大的那个数还要小,那么就输出最大的那个,之前的就不用管了。

    问题实现

    为了更好的实现我们的思路,我们可以开一个队列q,类型为long longqueue<long long> q

    说到这个队列里面存的数具体表示什么意义,还真不是一两句话就能说明白的。那么我们暂且抛开对q的具体定义,先来说一说开这个q的目的:

      用来暂存当前已得到的最大数值,在数组待选择的下一个数比队列元素小时使用队列中的元素,并弹出

    这样一来咱们的问题实现就很简单了,分为以下步骤:

    1. 使用给定的随机数生成器生成数据并存储
    2. 排序该数组
    3. 向q中推入目前得到的最大数
    4. 数组中没有更大的数可以选择时,调用q中元素直到数组元素选完(此时q中仅存的数字就是答案)
    5. 输出q.front()

    这题做完了

    巨大问题

    但是我们看到了这一题的数据范围非常不友好:n,m≤10^7 ……

    nlogn基本超了十的五次方就炸了,n的话能坚持到六次方 ——jiang25262

    那么我们回头来捋一捋我们学过的一些排序算法:

    • 冒泡
    • 插入
    • 选择
    • 快排
    • 归并
    • 基数(太玄学)
    • …………

    我们发现好像上面说到的方法都最多只能撑到1e5,这样的程序是不会AC的。但是结合我们上述的问题实现分析,我们起码可以写出一个及格(60分)的代码

    60分代码

    #include<iostream>
    #include<queue>
    #include<algorithm>
    #define ll long long
    #define rint register int
    using namespace std;
    const int maxn=1e7+10;
    ll cnt,n,m;
    int a[maxn],b[maxn];
    queue<ll>q;
    void generate_array(int n, int m, int seed) {
        unsigned x = seed;
        for (int i = 0; i < n; ++i) {
            x ^= x << 13;
            x ^= x >> 17;
            x ^= x << 5;
            a[i]=(x % m+ 1);
        }
    }
    
    inline ll get(){
        if(q.empty())
            return a[cnt++];
    
        ll f=q.front();
        if(cnt==n||a[cnt]>f){
            q.pop();
            return f;
        }
    
        return a[cnt++];
    }
    
    int main(){
        ios::sync_with_stdio(false);//使用最快速的读入(其实没用)
        int seed;
        cin>>n>>m>>seed;
        generate_array(n,m,seed);
    
        sort(a,a+n);//使用还可以的快排排序
    
        ll a1,a2;
        for(rint i=1;i<=n-1;i++){
            a1=get();a2=get();
            q.push(max(a1<<1,a2));
        }
        cout<<q.front()<<'\n';
        return 0;
    }
    

    问题解决

    仔细阅读我们的60分代码我们可以发现一个显而易见的问题,即:该程序的时间复杂度其实就是排序的时间复杂度。因为对于队列的操作都是O(1)的,获取元素的操作是O(n)的,排序的复杂度再低也低不到O(n)以下。故排序复杂度决定了这题的复杂度。

    所以我们现在亟待解决的就是排序问题。

    分析到现在,想必我们的目的已经非常明确:我们需要一个O(n)的排序算法!

    桶排!

    桶排的基本思路是这样,我们把它形象化:

      先开一个数组作为一个桶,然后把等待排序的元素倒进桶里。为了维护“倒进去”的过程,我们对于每一个元素按照其数值计数:

    b[a[i]]++;//其中a为待排序数组,b为桶
    

      接着我们需要找到这些数的最大值,从0开始(如果保证数据>0的话)依次枚举数字,以检查桶中是否有这个数字。如果桶中有,就倒出来,有几个就倒几个:

    int t=0;
        for(rint i=0;i<=m;++i)
            while(b[i]>=1){
                a[t++]=i;
                b[i]--;
            }
    

      倒完了,咱们也就排完了。上标准代码:

    for(int i=0;i<n;++i)
    		b[a[i]]++;
        int t=0;
        for(rint i=0;i<=m;++i)
            while(b[i]>=1){
                a[t++]=i;
                b[i]--;
            }
    //其中,n为元素个数,m为这些元素的最大值
    

    桶排就讲完了,讲完了桶排,这一题也就没有什么可以讲的了。我们接下来要做的只是把原来的sort(a,a+n)换成标准桶排就可以了。

    这题真正做完,最后奉上AC代码。

    AC代码

    #include<iostream>
    #include<queue>
    #define ll long long
    #define rint register int
    using namespace std;
    const int maxn=1e7+10;
    ll cnt,n,m;
    int a[maxn],b[maxn];
    queue<ll>q;
    void generate_array(int n, int m, int seed) {
        unsigned x = seed;
        for (int i = 0; i < n; ++i) {
            x ^= x << 13;
            x ^= x >> 17;
            x ^= x << 5;
            a[i]=(x % m+ 1);
        }
    }
    
    inline ll get(){
        if(q.empty())
    		return a[cnt++];
    
        ll f=q.front();
        if(cnt==n||a[cnt]>f){
    		q.pop();
    		return f;
    	}
    
        return a[cnt++];
    }
    
    int main(){
    	ios::sync_with_stdio(false);
        int seed;
        cin>>n>>m>>seed;
        generate_array(n,m,seed);
    
        for(rint i=0;i<n;++i)
    		b[a[i]]++;
        int t=0;
        for(rint i=0;i<=m;++i)
            while(b[i]>=1){
                a[t++]=i;
                b[i]--;
            }
    
        ll a1,a2;
        for(rint i=1;i<=n-1;i++){
            a1=get();a2=get();
            q.push(max(a1<<1,a2));
        }
        cout<<q.front();
        return 0;
    }
    

    鸣谢

    教会了我桶排的 @晨搏的小迷妹 U84254

    部分代码提供 @晨搏的小迷妹 U84254

    复杂度简要区分 @jiang25262 U50227

    最后宣传一下我们的团队

    欢迎加入我们的团队:洛谷团队传送门

    刚刚入门的oier们可以在这里得到最基础的技能培训辅导,我们随时可以为入门级的oier们答疑解惑。进入团队即可获得本团队人员录制的线上课程,主要针对竞赛入门向。在语法课程结束后我们会开启算法线上课程,敬请期待!

    当然也非常欢迎各路大佬神犇光临出题虐菜(主要是虐我,他们都比我强)

    私信我获取团队QQ群号和线上课程地址

    感谢阅读

    • 1

    信息

    ID
    3795
    时间
    2000ms
    内存
    250MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者