Luogu-P2527 (模拟出单调队列)

本文介绍了一个有趣的问题:求由特定质因数组成的数列中第k小的数。通过两种不同的实现方法进行了探讨,一种使用了普通数组模拟优先队列,另一种则直接利用了优先队列的数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Problem Description

panda是个数学怪人,他非常喜欢研究跟别人相反的事情。最近他正在研究筛法,众所周知,对一个范围内的整数,经过筛法处理以后,剩下的全部都是质数,不过panda对这些不感兴趣,他只对被筛掉的数感兴趣,他觉得在这些被筛掉的数中一定隐藏着重要的宇宙秘密,只是人们还没有发现罢了。

panda还觉得如果只是单纯地从小到大筛的话,还不足够发现其中的奥秘,于是他决定对至多只包含某些质因数的数进行研究(比如说至多只包含质因数2,3的数有2,3,4,6,8,9,……),他需要得到这些数中第k小的数(k是panda认为的宇宙系数),请你编个程序,帮助他找到这个数。

Input

第1行有2个数n,k,n代表质因数的个数,k代表那个宇宙系数(1<=n<=100,1<=k<=100000)

第2行有n个数,代表这n个质因数。(每个均小于1000,且不相同)

Output

仅1行,即至多只包含这n个质因数的数中第k小的数。(这个数不会超过2000000000)

Sample Input

2 7
3 5

Sample Output

45(前6个分别是3,5,9,15,25,27)

分析

  • 正常的想法是把乘出的数放入一个优先队列里,每次取出最小值和给出的 n 个素数相乘放入队列,再判一判重即可。时间复杂度大概为 k*log(2,n) ,其实处理得好也能过,优化一下空间即可,下面有一个我们同学打的,由于没处理好就 MLE 爆空间了。
  • 鉴于 n 十分小,k 又十分大,而且发现 n*k 的时间也是可以的,于是就想想用普通数组来模拟出优先队列。我用了个 b[i] 来记录第 i 个素数当前乘到了 ans[] 中第几个数(好像是下一个应该乘的数),然后每次要加一个数到 ans[] 中时,对每个素数乘一下它下一个要乘的,取出最小值,判一下重,如果无恙就放到 ans[] 后面即可。
  • 这样就保证了 ans[] 数组中的元素是单调递增的,而且不会跳元素。
  • 具体一点,ans[] 起初放个 1 进去就可以了,最后得到 k 个数(不包括那个 1)就行了,输出第 k 个数。

程序

这个是 Ac 了的程序。

#include <cstdio>
int n,k,cnt;
int a[1005],b[1005],ans[100005];

int main(){
    scanf("%d%d",&n,&k);
    for (int i=1; i<=n; i++) scanf("%d",&a[i]);
    ans[0]=1;
    while (cnt<k){
        int Min=2147483647,Minx;
        for (int i=1; i<=n; i++)
            if (ans[b[i]]*a[i]<Min){
                Min=ans[b[i]]*a[i];
                Minx=i;
            }
        b[Minx]++;
        if (Min!=ans[cnt]) ans[++cnt]=Min;
    }
    printf("%d",ans[k]);
}

这个是一个同学写的,用了优先队列 STL,结果处理不好空间爆了。

#include<cstdio>
#include<iostream>
#include<queue>
#define N 110
using namespace std;
typedef long long LL;
priority_queue<LL,vector<LL>,greater<LL> >Q;
LL ans,x,pre;
int n,m,a[N],tot;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        Q.push(a[i]);
    }
    while(!Q.empty()&&tot<m){
        x=Q.top();
        Q.pop();
        if(x==pre)continue;
        tot++;
        ans=x;
        for(int i=1;i<=n;i++) Q.push(x*a[i]);
        pre=x;
    }
    cout<<ans;
}

提示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值