对于一类对于约数个数问题大小的最大值以及趋向大值的求解

例题:
给出上限n,求第k个小于等于n的数满足小于该数的约数个数比它大的数不超过x个.

n<=1e18 , x<= 233

根据ZJOI反质数的经验,我们可以利用很少的质数来更新解决此题.
很容易知道以下性质:
我们对于一个质数p考虑x小于p质数相乘所得的集合G,对于G中的所有元素如果现在没有被我们选中那么显然
x * p ^ y(y>=0) 也一定不会被我们选中.所以我们利用上一个筛选序列乘上若干个当前质数加入候选序列,从小到大排序,维护前k大的因子个数每次加入时比较更新一下即可.

由于大质数的不优性我们可以利用这种乱搞方法高正确率的做出结果.

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

//math problem when requires about sth about the numbers of divisors then you can limit the number of primes to get AC.
//yusing the stagety that slowly extends the num of the primes and diedai. 
//We'll first use the larger primes' bad xingzhi to decrease the range .
//We'll sort the possible choices , so that we'll just care about the last one's divisor number.And then we update it , and reupdate the queue.
//go to have a zongjie

typedef long long LL;

const LL N = 1000005;
const LL M = 234;

#define Pair pair<LL,int>

bool isp[305];
LL  n , m , t;
int mx[N] , cnt , tot , pr[305] , all;

Pair ans[N] , Nowqueue[N];

void prework(void) {
    memset(isp , 1,sizeof(isp)); isp[1] = 0;
    for(LL i = 1;i <= 300;i ++)  {
        if(isp[i]) pr[++ cnt] = i;
        for(LL j = 1;j <= cnt && pr[j] * i <= 300;j ++) {
            isp[pr[j] * i] = 0;
            if(pr[j] % i == 0) break;
        }
    }
}

bool pd(LL x , LL y) {
    LL it = 0;
    while(y) {
        if(y & 1) it += x;
        y >>= 1;
        if(it > n || x > n) return 0; 
        x *= 2;
    }
    return 1;
}

void update(int x) {
    int p = m;
    while(p && x >= mx[p - 1]) p --;
    for(LL i = m;i > p;i --) mx[i] = mx[i - 1]; 
    mx[p] = x;
}

void dothat(void) {
    prework();
    ans[++ tot] = make_pair(1 , 1);
    for(int i = 1;i <= cnt;i ++) {
        all = 0; LL wh = pr[i];
        for(int j = 1;j <= tot;j ++) {
            Nowqueue[++ all] = ans[j];
            LL now = wh , w = 1;
            while(pd(Nowqueue[all].first , wh)) {
                all ++;
                Nowqueue[all] = make_pair(Nowqueue[all - 1].first * now , ans[j].second * (w + 1));
                w ++; if(now > n) break;
            }
        }
        sort(Nowqueue + 1 , Nowqueue + all + 1);
        memset(mx , 0 ,sizeof(mx)); 
        tot = 0;
        for(LL j = 1;j <= all;j ++) {
            if(Nowqueue[j].second >= mx[m]) {
                update(Nowqueue[j].second); ans[++ tot] = Nowqueue[j];
            }
        }
    }
}

int main(void) {
    scanf("%lld%lld%lld",&t,&m,&n);
    dothat();
    int x;
    for(int i = 1;i <= t;i ++) {
        scanf("%d",&x);
        if(x > tot) puts("-1");
        else printf("%lld\n",ans[x].first);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值