7809 - 试题J:因数个数 25'(欧拉筛法求因子个数)

链接:http://oj.hzjingma.com/p/7809?view=classic
来源:竞码编程

题目描述
  求所有 2 2 n n 的整数中,因数个数第 k k 少的数因数个数是多少。
输入描述
  第一行两个正整数 n n , k k 即题目描述中的 n n , k k
输出描述
  输出仅一行,即因数个数第 k k 少的数因数个数。
输入
  10 5
输出
  3

  解法一:直接暴力求解, s q r t ( n ) sqrt(n) 判断因子个数。复杂度 O ( n n ) O(n√n) ,对于此题目来说会 T L E TLE

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e7 + 10;
ll cnt[maxn];
set<ll>s;

ll cal(int x) {
    ll cnt1 = 2;
    if(x == 1)
        return 1;
    for(ll i = 2; i * i <= x; i++) {
        if(x % i == 0) {
            if(i * i == x)
                cnt1++;
            else
                cnt1 += 2;
        }
    }
    return cnt1;
}

int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    for(int i = 2; i <= n; i++)
        cnt[i] = cal(i);
    sort(cnt + 1, cnt + n + 1);
    //for(int i=2;i<=n;i++) cout<<cnt[i]<<" "<<endl;
    printf("%lld\n", cnt[k + 1]);
    /*s.insert(cal(i));
    int cnt2=0;
    for(set<ll>::iterator it=s.begin();it!=s.end();it++) cnt[cnt2++]=*it;
    sort(cnt,cnt+cnt2);*/

    return 0;
}

  解法二:用类似于埃式筛法的方法从 2 n 2-n 此把每个数的倍数因子 + + ++ s o r t sort 之后直接输出结果。复杂度: O ( n l o g n ) O(nlogn) ,这个 o j oj 能跑过去 1 e 8 1e8 的代码。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e7 + 10;
int cnt[maxn];

inline int read() {
    int x = 0, neg = 1;
    char op = getchar();
    while (!isdigit(op)) {
        if (op == '-')
            neg = -1;
        op = getchar();
    }
    while (isdigit(op)) {
        x = 10 * x + op - '0';
        op = getchar();
    }
    return neg * x;
}
inline void print(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x >= 10)
        print(x / 10);
    putchar(x % 10 + '0');
}

int main() {
    int n, k;
    n = read();
    k = read();
    if(n >= maxn)
        puts("2");
    else {
        for(int i = 2; i <= n; i++) {
            cnt[i] += 2;
            for(int j = 2 * i; j <= n; j += i)
                cnt[j]++;
        }
        sort(cnt + 1, cnt + n + 1);
        print(cnt[k + 1]);
    }
    return 0;
}

  解法三:我们可以知道,对于每一个数 n n 都可以分解成如下的式子
   n = p 1 k 1 × p 2 k 2 × p 3 k 3 × × p x k x × n = p_{1}^{k_{1}} × p_{2}^{k_{2}} ×p_{3}^{k_{3}} × ···· × p_{x}^{k_{x}} × ····
   p p 表示质因子, k k 表示质因子的个数
  那个就有 n n 的因子个数 c n t [ n ] = ( k 1 + 1 ) × ( k 2 + 1 ) × ( k 3 + 1 ) × × ( k 4 + 1 ) × cnt[n] = (k1+1) × (k2+1) × (k3+1) × ···· × (k4+1) × ····
  在欧拉筛的过程中, i i 为素数的时候 c n t [ i ] = 2 cnt[i] = 2 (素数只有两个因子),此时我们在使用一个数组 t i m e s times 来表示 i i 的最小质因子的个数,此时 t i m e s [ i ] = 1 times[i] = 1 ,这个素数当前的质因子只有 1 1 个。
i % p r i m e [ j ] = = 0 i \% prime[j] == 0 的时候
   i = p 1 k 1 × p 2 k 2 × p 3 k 3 i = p_{1}^{k_{1}} × p_{2}^{k_{2}} ×p_{3}^{k_{3}}
  那么 i p r i m e [ j ] i * prime[j] 就是 i i 中的最小质因子的个数加一
  此时就有 t i m e s [ i p r i m e [ j ] ] = t i m e s [ i ] + 1 times[i * prime[j]] = times[i] + 1 i i 的 最小质因子数加一就是 i p r i m e [ j ] i * prime[j] 的最小质因子的个数,此时这个数的因子个数就会随之改变,就变成了 c n t [ i p r i m e [ j ] ] = ( k 1 + 1 + 1 ) × ( k 2 + 1 ) × ( k 3 + 1 ) × × ( k 4 + 1 ) × cnt[i*prime[j]] = (k1+1+1) × (k2+1) × (k3+1) × ···· × (k4+1) × ···· k 1 + 1 k1+1 是原本 i i 的最小质因子的个数, k 1 + 1 + 1 k1+1+1 则是现在这个数的最小质因子的个数,所以就可以得到 c n t [ i p r i m e [ j ] ] = c n t [ i ] / ( t i m e s [ i ] + 1 ) ( t i m e s [ i p r i m e [ j ] ] + 1 ) cnt[i*prime[j]] = cnt[i]/(times[i]+1)*(times[i*prime[j]]+1) 加一表示 1 1 也是这个数的因子,然后退出这层循环,因为后面的数会在遍历其他数字的时候被筛去。
i % p r i m e [ j ] ! = 0 i \% prime[j] != 0 的时候
  说明 i i 中没有这个这个素数,那么 i p r i m e [ j ] i*prime[j] 就多了一种素数,这个素数出现的次数 t i m e s [ i p r i m e [ i ] ] = 1 times[i*prime[i]] = 1 ,因子个数 c n t [ i p r i m e [ i ] ] = c n t [ i ] ( t i m e s [ i p r i m e [ i ] ] + 1 ) = c n t [ i ] 2 cnt[i*prime[i]] = cnt[i] * (times[i*prime[i]] + 1) = cnt[i] * 2
复杂度: O ( n ) O(n)

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e7 + 10;
int cnt[maxn] = {0, 1}, times[maxn], prime[maxn], num1[maxn]; //最小质因子出现次数
bool is_prime[maxn];

inline int read() {
    int x = 0, neg = 1;
    char op = getchar();
    while (!isdigit(op)) {
        if (op == '-')
            neg = -1;
        op = getchar();
    }
    while (isdigit(op)) {
        x = 10 * x + op - '0';
        op = getchar();
    }
    return neg * x;
}
inline void print(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x >= 10)
        print(x / 10);
    putchar(x % 10 + '0');
}

void solve() {
    for(int i = 2; i <= maxn; i++)
        is_prime[i] = true;
    is_prime[0] = is_prime[1] = false;
    int num = 0;
    for(int i = 2; i <= maxn; i++) {
        if(is_prime[i]) {
            cnt[i] = 2;
            times[i] = 1;
            prime[num++] = i;
        }
        for(int j = 0; j < num; j++) {
            if(i * prime[j] > maxn)
                break;
            is_prime[i * prime[j]] = false;
            if(i % prime[j] == 0) { //当前素数是最小质因子
                times[i * prime[j]] = times[i] + 1;
                cnt[i * prime[j]] = cnt[i] / (times[i] + 1) * (times[i * prime[j]] + 1);
                break;
            }
            times[i * prime[j]] = 1;
            cnt[i * prime[j]] = cnt[i] * 2;
        }
    }
}

int main() {
    solve();
    int n, k;
    n = read();
    k = read();
    if(n >= maxn)
        puts("2");
    else {
        //for(int i=2;i<=n;i++) cout<<cnt[i]<<" "; cout<<endl;
        for(int i = 2; i <= n; i++)
            num1[cnt[i]]++;
        //for(int i=2;i<=n;i++) cout<<num1[i]<<" "; cout<<endl;
        int cnt1 = 0;
        for(int i = 2; i <= n; i++) {
            while(num1[i]--)
                ++cnt1;
            if(cnt1 >= k) {
                print(i);
                break;
            }
        }
    }
    return 0;
}
发布了166 篇原创文章 · 获赞 68 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_42217376/article/details/104315746