CCF-CSP 202312-2 因子化简

CCF-CSP 202312-2 因子化简

  • 题目要求
    • ‍⬛题目背景
    • ‍⬛问题描述
    • ‍⬛输入格式
    • ‍⬛输出格式
    • ‍⬛样例输入
    • ‍⬛样例输出
    • ‍⬛样例解释
    • ‍⬛子任务
  • 问题解决
    • 满分代码(含逐行代码解释)
      • C++
      • Python
    • 场景拓展

题目要求

‍⬛题目背景

质数(又称“素数”)是指在大于 1 1 1 的自然数中,除了 1 1 1 和它本身以外不再有其他因数的自然数。

‍⬛问题描述

小 P 同学在学习了素数的概念后得知,任意的正整数 n n n 都可以唯一地表示为若干素因子相乘的形式。如果正整数 n n n m m m 个不同的素数因子 p 1 , p 2 , ⋅ ⋅ ⋅ , p m p_1,p_2,···,p_m p1,p2,⋅⋅⋅,pm,则可以表示为: n = p 1 t 1 × p 2 t 2 × ⋅ ⋅ ⋅ × p m t m n={p_1}^{t_1}×{p_2}^{t_2}×···×{p_m}^{t_m} n=p1t1×p2t2×⋅⋅⋅×pmtm

小 P 认为,每个素因子对应的指数 t i t_i ti 反映了该素因子对于 n n n 的重要程度。现设定一个阈值 k k k ,如果某个素因子 p i p_i pi 对应的指数 t i t_i ti 小于 k k k ,则认为该素因子不重要,可以将 p i t i {p_i}^{t_i} piti 项从 n n n 中除去;反之则将 p i t i {p_i}^{t_i} piti 项保留。最终剩余项的乘积就是 n n n 简化后的值,如果没有剩余项则认为简化后的值等于 1 1 1

试编写程序处理 q q q 个查询:

  • 每个查询包含两个正整数 n n n k k k ,要求计算按上述方法将 n n n 简化后的值。

‍⬛输入格式

从标准输入读入数据。
输入共 q + 1 q+1 q+1 行。
输入的第一行包含一个正整数 q q q,表示查询的个数。
接下来 q q q 行每行包含两个正整数 n n n k k k,表示一个查询。

‍⬛输出格式

输出到标准输出。
输出共 q q q 行。
每行输出一个正整数,表示对应的查询结果。

‍⬛样例输入

3
2155895064 3
2 2
10000000000 10

‍⬛样例输出

2238728
1
10000000000

‍⬛样例解释

查询一:

  • n = 2 3 × 3 2 × 2 3 4 × 107 n=2^3×3^2×23^4×107 n=23×32×234×107
  • 其中素因子 3 3 3 指数为 2 2 2 107 107 107 指数为 1 1 1。将这两项从 n n n 中除去后,剩余项的乘积为 n = 2 3 × 2 3 4 = 2238728 n=2^3×23^4=2238728 n=23×234=2238728

查询二:

  • 所有项均被除去,输出 1 1 1

查询三:

  • 所有项均保留,将 n n n 原样输出。

‍⬛子任务

40%的测试数据满足: n ≤ 1000 n \leq 1000 n1000
80%的测试数据满足: n ≤ 1 0 5 n \leq 10^5 n105
全部的测试数据满足: 1 < n ≤ 1 0 10 1 < n \leq 10^{10} 1<n1010 1 < k 1 < k 1<k q ≤ 10 q \leq 10 q10

问题解决

满分代码(含逐行代码解释)

C++

#include
using namespace std;

bool domains[100000001]; //domains布尔数组,用于标记数字是否为素数
int prime[100000001], tot; //prime整用于存储素数,tot用于记录素数的个数

void sieve(int n){ //筛法(Sieve)计算素数个数 
	for(int i = 2; i*i <= n; i++){ //筛法重要思想:从2开始,遍历到不大于n的平方根即可 
	/*
		对于大规模数组,适用于筛法求解,即for(int i=2; i*i<=n; i++)
		对于小规模数组,不适用于筛法求解,可用for(int i=2; i <=n; i++)
	*/
        if(domains[i] == false){ //如果domains[i]为 false,则表示i是素数,从素数2开始遍历,默认初始为false 
            prime[++tot] = i ; //如果i是素数,将 i 添加到素数数组prime中,并且素数数量tot加一
            for(int j = i; i*j <= n; j++) //对遍历到的每一个i,标记i的倍数必定不是素数
                domains[i*j] = true; //如果i是素数,将domains[i*j]设置为 true
        }
    }
	return;
}

int main()
{
	sieve(100000001); //首先利用筛法找出所有的素数 
	int q, k; 
	long long n; //注意这里的正整数n可能很大 
	cin >> q; //输入要处理的q个查询 
	for(int i = 0; i < q;i++){
		cin >> n >> k; //输入每个查询中的正整数n及阈值k
		long long result = 1, power = 0, temp = 1; //result表示最终结果,power表示素数的幂,temp表示中间素数乘积的结果 
		for(int i = 1; i <= tot; i++) //注意tot是从1开始计数的,所以这里只能从i=1开始遍历所有素数 
		{
			power = 0, temp = 1; //每次循环还是前将幂和中间结果乘积重置,这表示对每一个素数单独处理计算,这一点很重要!!!
			while(n % prime[i] == 0) //因为要把正整数n分解为众多素数的乘积,所以n是素数的整数倍时进入循环 
			{
				n = n / prime[i]; //更新除以该因数素数之后的n,也即因式分解过程 
				temp = temp * prime[i]; //更新该因数素数出现所有次数后的乘积,也即因式合成过程  
				power++; //该因数素数的幂加一 
			}
			if(power >= k) //如果当前素数prime[i]的出现次数(幂值)power大于等于阈值k
			result = result * temp; //最终结果累乘间素数乘积的结果
		}
		cout << result << endl;
	}
	return 0;
 }  

Python

'''
代码逻辑与C++完全相同,不再一一逐行赘述
'''
import numpy as np 
# 使用NumPy数组代替列表,并使用NumPy提供的函数处理筛选素数的过程

def sieve(n):
    domains = np.zeros(n+1, dtype=bool)
    prime = []
    for i in range(2, int(np.sqrt(n))+1):
        if not domains[i]:
            prime.append(i)
            domains[i*i:n+1:i] = True
    return prime

q = int(input())
primes = sieve(100000001)

for _ in range(q):
    n, k = map(int, input().split())
    result = 1
    for p in primes:
        power = 0
        while n % p == 0:
            n //= p
            power += 1
        if power >= k:
            result *= p ** power
    print(result)

场景拓展

本题代码可以用于解决以下问题:

  • 求解素数:使用筛法(Sieve)计算指定范围内的素数,将其存储到数组中。
  • 因式分解:将一个正整数分解为若干素数的乘积,可以在筛法求解素数的基础上实现。
  • 素数幂次问题:给定一个正整数n和一个阈值k,找出n的因子中所有素数的幂次都不小于k的最小乘积。

你可能感兴趣的:(CCF-CSP,算法,c++,python)