CCF CSP题解:因子化简(202312-2)

链接和思路

OJ链接:传送门

问题重述

本题基于一个基本事实,即任何一个大整数 n n n都可以唯一地分解为如下形式
n = p 1 t 1 × p 2 t 2 × ⋯ × p m t m n = p_1^{t_1} \times p_2^{t_2} \times \cdots \times p_m^{t_m} n=p1t1×p2t2××pmtm其中, p 1 , p 2 , ⋯   , p m p_1, p_2, \cdots, p_m p1,p2,,pm表示 m m m个素因子,素因子上的幂 t i > 0 ( 1 ≤ i ≤ m ) t_i > 0 (1 \le i \le m) ti>0(1im)

本题给出 q q q n , k n,k n,k,满足 1 < n ≤ 1 0 10 , 1 < k , q ≤ 10 1 < n \le 10^{10}, 1 < k, q \le 10 1<n1010,1<k,q10,要求对每组 n , k n,k n,k,求出 n n n除以所有满足 t i < k t_i<k ti<k p i t i p_i^{t_i} piti后的结果,即求出
n ∏ 1 ≤ i ≤ m 且 t i < k p i t i = ∏ 1 ≤ i ≤ m 且 t i ≥ k p i t i \frac{n}{\prod_{1 \le i \le m 且 t_i<k}p_i^{t_i}} = \prod_{1 \le i \le m 且 t_i \ge k}p_i^{t_i} 1imti<kpitin=1imtikpiti

求解思路

由于 n n n较大,求出小于 n n n的所有的素数是很困难的。因此,我们考虑使用素数筛先求出 1 0 5 10^5 105以内的所有素数。如果出现有 p i > 1 0 5 p_i>10^5 pi>105的情况,则对应的 t i t_i ti必不可能大于1,只能等于1,且这样的 p i p_i pi最多只可能有1个。常见的素数筛有埃氏筛、欧拉筛等。本文使用埃氏筛,其实现如下:

static vector<long long> primes;
// 埃拉托斯特尼筛法,时间复杂度为O(n * loglogn)
void Find_Prime_number(long long n = N){
    vector<bool> flag(n + 1, true);
    //0和1不是素数,直接初始化好
    flag[0] = 0;
    flag[1] = 0;
    //从2开始,1不是素数
    for (long long i = 2; i <= n; i++){
        //如果当前数字是素数
        if (flag[i]){
            //i的倍数标记被不是素数
            for (long long j = i * i; j <= n; j += i)
                flag[j] = false;
            primes.push_back(i);
        }
    }
}

我们将素数筛获得的所有的 1 0 5 10^5 105以内的素数保存在vector<long long> primes中。然后,将 n n n依次尝试除以primes中的每个质因子 p i p_i pi。如果能够整除 p i p_i pi的次数大于等于 k k k,则说明 p i p_i pi是“重要的”,因此恢复到尝试除以 p i p_i pi之前的值。遍历完primes后,我们再处理可能存在的大于 1 0 5 10^5 105的质因子。

上文提到,如果存在 p i > 1 0 5 p_i > 10^5 pi>105,在本题的设定中,最多只可能有1个,且对应的 t i t_i ti只可能为1,由于 k > 1 = t i k>1=t_i k>1=ti,最终答案要剔除掉 p i t i p_i ^{t_i} piti

因此,我们需要引入如下函数,用以辅助判断 n n n除以 ∏ 1 ≤ i ≤ m 且 p i ≤ 1 0 5 p i t i \prod_{1 \le i \le m 且 p_i \le 10^5}p_i^{t_i} 1impi105piti后,是否是一个大于 1 0 5 10^5 105素数。如果是,最终结果应该除以这个素数。

bool is_prime(long long n){
    for (long long i = 2; i * i <= n; i++)
        if(n % i == 0)
            return false;
    return true;
}

AC代码

满分代码如下,使用CPP 11标准即可编译运行。
在这里插入图片描述

#include <iostream>
#include <vector>
using namespace std;

const long long N = 1e5+5;

static vector<long long> primes;

// 埃拉托斯特尼筛法,时间复杂度为O(n * loglogn)
void Find_Prime_number(long long n = N){
    vector<bool> flag(n + 1, true);
    //0和1不是素数,直接初始化好
    flag[0] = 0;
    flag[1] = 0;
    //从2开始,1不是素数
    for (long long i = 2; i <= n; i++){
        //如果当前数字是素数
        if (flag[i]){
            //i的倍数标记被不是素数
            for (long long j = i * i; j <= n; j += i)
                flag[j] = false;
            primes.push_back(i);
        }
    }
}

bool is_prime(long long n){
    for (long long i = 2; i * i <= n; i++)
        if(n % i == 0)
            return false;
    return true;
}

int main() {
    Find_Prime_number();
    int q;
    cin >> q;
    while(q--) {
        long long  n;
        int k;
        cin >> n >> k;
        long long flag = n;
        for (long long prime: primes) {
            long long tmp = n;
            int cnt = 0;
            while (n % prime == 0){
                n /= prime;
                flag /= prime;
                cnt++;
            }
            if (cnt >= k)   // 如果t_i足够大,说明不需要除以p_i
                n = tmp;    // 恢复
        }
        if (flag > N && is_prime(flag)){
            // 如果存在大于N的质因子p_i,其对应的t_i=1<k,需要去除
            n /= flag;
        }
        cout << n << endl;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曹无悔

请支持我的梦想!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值