c语言N的阶乘质因子分解,牛客算法周周练4 C题——阶乘 二分+阶乘分解质因子...

博客介绍了如何通过二分查找和质因数分解来寻找最小的n,使得n!是给定整数p的倍数。详细解释了如何分解质因数,如何计算阶乘中质因子的个数,并提供了两种不同的解决方案:一种使用二分查找和质因数计数,另一种利用最大公因子进行剪枝。还特别提到了在实现过程中的一些优化技巧,如避免超时错误和剪枝策略。

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

链接 : 题目

题意 : 给定一个p, 找一个最小的n,使得n!是p的倍数。

思路 :有两种思路

1、二分方法,因为要找到最小的n!,用二分即可。重点便是二分里的judge函数。

(1)此题要用到分解质因子的方法。

算数基本定理 : 任意一个大于1的自然数N,如果N不为质数,则N可以唯一分解为有限个质数的乘积。

比如 100 = 2 * 2 * 5 * 5。

这里我们要用map这个容器,first是每个质因数,2和5,second是出现的次数,将100分解后,map里存在的是 {2,2,} 和 {5, 2}。

然后我们需要判断一个 mid 的阶乘是否是100的倍数,就要看mid !里面质因子2的个数 >= 2, 5的个数 >= 2。

(2) 那么如何判断 n!里面质因子的个数呢?

n!里面质因子p的个数,1-n中包含质因子p的数的个数。

n!中至少包含一个质因子p的个数是 n / p,至少包含两个的个数是n / p^2……

比如 mid = 8,判断8!里面质因数2的个数。

首先 8! 里面有2 4 6 8 四个数可以整除2,此个数便是8 / 2 = 4;除2 后 8!里面,就变成了 1 2 3 4,其中2 和 4可以整除2,此个数便是 4 / 2 = 2; 除2后只有一个2, 此个数便是 2 / 2 = 1。 所以 8! 里面 质因子2的个数便是 4 + 2 + 1 = 7。

(3)代码:

#include "bits/stdc++.h"

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))

#define pb push_back

// #define _DEBUG

typedef long long ll;

const int maxn = 2e5 + 7;

const int INF = INT_MAX; //常量代替 0x7fffffff

int t,p;

map mp;

void init(int p){//分解质因数并记录在map里

mp.clear();

for (int i = 2; i*i <= p; i++) {

// i*i 是一个优化,因为p的因子最大是 根号p

while (p % i == 0) {

mp[i] ++;

p /= i;

}

if (p == 1) {

break;

}

}

if (p != 1) {//p本身是质数

mp[p] = 1;

}

}

bool ok(int mid){

for (map::iterator it = mp.begin(); it != mp.end(); it++) {

int prime = it->first, num = it->second;

int cnt = 0, n = mid;//cnt是每个质因数的个数

while (n) {

cnt += n / prime;

n /= prime;

}

if (cnt < num) {

return false;

}

}

return true;

}

int main(){

#ifdef _DEBUG

freopen("in.txt","r",stdin);

freopen("out.txt","w",stdout);

#endif

ios::sync_with_stdio(0);

cin.tie(0);

cin>>t;

while (t--) {

cin>>p;

init(p);

int l = 1, r = p;

int ans = 0;

while (l <= r) {

int mid = l + (r - l) / 2;

if (ok(mid)) {

r = mid - 1;

ans = mid;//每次更新ans

}else{

l = mid + 1;

}

}

cout<

}

return 0;

}

(4)遇到的问题 :

对p分解质因子的时候,注意是 i*i <= p ,否则会TLE。这是一种类似埃氏筛法的小技巧,具体可以看如下链接:

欧拉筛法

二分里面,如果r = mid 而不是 mid-1,会死循环,所以就直接mid + 1和mid - 1这样,同时最后的l 或者 r可能不是最后答案,就用ans每次更新答案。

2、同样是分解质因子的方法,不过是用gcd

(1)最大公因子 Greatest Common Divisor

此函数在刘汝佳大神的《算法竞赛入门经典》中有讲到。辗转相除法。

int gcd(int a,int b){

if(b == 0) return a;

return gcd(b,a%b);

}

(2)如果p是1或者质数,则直接输出p;

if (p == 1 || isPrime(p)) {

cout<

}else{

for (int i = 2; ; i++) {

if (gcd(i,p) != 1) {

p /= gcd(i,p);

if (p == 1) {

cout<

break;

}

if (i < p && isPrime(p)) {

cout<

break;

}

}

}

}

i 从2开始遍历,如果i 和p 有最大公因子, p /= gcd(i,p),如果p = 1,那么直接输出这个i; 如果此时i < p, 并且p是质数,则直接输出p。这是一个剪枝,因为p 是质数,i < p,那么i 递增到p后才会满足 p == 1,所以直接输出p。

其思想便是分解质因子,因为i是从2开始递增的,2——i ,相当于是 i 的阶乘,每个数判断是否有p中的质因子,如果有就更新p,最后 p = 1则说明 此时的i的阶乘便是p的倍数。这样最后输出的i 便满足条件。

(3)代码:

#include "bits/stdc++.h"

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))

#define pb push_back

typedef long long ll;

const int maxn = 1e4 + 7;

const int INF = INT_MAX;

// #define _DEBUG

int t,p;

bool isPrime(int p){

if(p == 1) return false;

for (int i = 2; i*i <= p; i++) {

if(p % i == 0) return false;

}

return true;

}

int gcd(int a,int b){

if(b == 0) return a;

return gcd(b,a%b);

}

int main(){

#ifdef _DEBUG

freopen("in.txt","r",stdin);

freopen("out.txt","w",stdout);

#endif

ios::sync_with_stdio(0);

cin.tie(0);

cin>>t;

while (t--) {

cin>>p;

if (p == 1 || isPrime(p)) {

cout<

}else{

for (int i = 2; ; i++) {

if (gcd(i,p) != 1) {

p /= gcd(i,p);

if (p == 1) {

cout<

break;

}

if (i < p && isPrime(p)) {

cout<

break;

}

}

}

}

}

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值