牛客算法周周练4 C题——阶乘 二分+阶乘分解质因子

  • 链接 : 题目
  • 题意 : 给定一个p, 找一个最小的n,使得n!是p的倍数。
  • 思路 :有两种思路
    1、二分方法,因为要找到最小的n!,用二分即可。重点便是二分里的judge函数。
    (1)此题要用到分解质因子的方法。

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

比如 100 = 2 * 2 * 5 * 5。
这里我们要用map<int, int> 这个容器,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<int,int> 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<int,int>::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<<ans<<endl;
    }
    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<<p<<endl;
        }else{
    
    
            for (int i = 2; ; i++) {
    
    
                if (gcd(i,p) != 1) {
    
    
                    p /= gcd(i,p);
                    if (p == 1) {
    
    
                        cout<<i<<endl;
                        break;
                    }
                    if (i < p && isPrime(p)) {
    
    
                        cout<<p<<endl;
                        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<<p<<endl;
        }else{
    
    
            for (int i = 2; ; i++) {
    
    
                if (gcd(i,p) != 1) {
    
    
                    p /= gcd(i,p);
                    if (p == 1) {
    
    
                        cout<<i<<endl;
                        break;
                    }
                    if (i < p && isPrime(p)) {
    
    
                        cout<<p<<endl;
                        break;
                    }
                }
            }
        }
    }
    return 0;
}

おすすめ

転載: blog.csdn.net/qq_39763472/article/details/105841041