与阶乘有关的几个问题(随时补充)

(文章补充于2020年3月19日22点22分)
1.求阶乘的因子个数
Factors of Factorial
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
You are given an integer N. Find the number of the positive divisors of N!, modulo 109+7.

Constraints
1≤N≤103
输入
The input is given from Standard Input in the following format:
N
输出
Print the number of the positive divisors of N!, modulo 109+7.
样例输入 Copy
3
样例输出 Copy
4
提示
There are four divisors of 3! =6: 1, 2, 3 and 6. Thus, the output should be 4.

#include<stdio.h>
const int mod=1e9+7;
long long int f(int x,int p)
{
    long long int ans;
    if(x<p)
    {
        ans=0;
    }
    else
    {
        ans=x/p+f(x/p,p);
    }
    return ans;
}
int isprime(int x)
{
    int flag=1;
    int i;
    for(i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            flag=0;
            break;
        }
    }
    return flag;
}
int main()
{
    int n;
    int i;
    long long int ans=1;
    scanf("%d",&n);
    for(i=2;i<=n;i++)
    {
        if(isprime(i))
        {
            ans=ans*(1+f(n,i))%mod;
        }
    }
    printf("%lld",ans);
    return 0;
}

原理:
唯一分解定理,也叫算术基本定理,指的是任何n>=2,都可以分解为,其中为质数。
其包括两个断言:
断言1:数n可以以某种方式分解成素数乘积。
断言2:仅有一种这样的因数分解。(除因数重排外)。
设n可分解为,其中为质数,设f(n)表示n的因子数,则
在这里插入图片描述

long long int f(int x,int p)
{
    long long int ans;
    if(x<p)
    {
        ans=0;
    }
    else
    {
        ans=x/p+f(x/p,p);
    }
    return ans;
}

此部分代码用于求解f(n),可结合这个例子来理解:
180=2*2*3*3*5=2^2x*3^2x5
180的约数个数为(1+2)(1+2)(1+1)=18个。
若求A/B的约数个数,A可分解为p1^a1 * p2^a2 * … * pk^ak , B可分解为 q1 ^b1 * q1^b2* …* qk^bk,则A/B的约数个数 为(a1-b1+1)(a2-b2+1)(a3-b3+1)…(ak-bk+1).
然后说N的阶乘:
例如:20!
1.先求出20以内的素数,(2,3,5,7,11,13,17,19)
2.再求各个素数的阶数
e(2)=[20/2]+[20/4]+[20/8]+[20/16]=18;
e(3)=[20/3]+[20/9]=8;
e(5)=[20/5]=4;

e(19)=[20/19]=1;
所以
20!=2^18 x 3^8 x 5^4 x x19^1
解释:
2、4、6、8、10、12、14、16、18、20能被2整除
4、8、12、16、20能被4整除(即被2除一次后还能被2整除)
8、16能被8整除(即被2除两次后还能被2整除)
16能被16整除(即被2除三次后还能被2整除)
这样就得到了2的阶。其它可以依次递推。
以上用于解释的例子参考自https://blog.csdn.net/ji414341055/article/details/5759157

2.求阶乘的位数
阶乘位数
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
在很多软件中需要用到较大的整数。比如一些软件将大数用于数据安全传送的密匙或者密码编译等等。在这个问题中,你要根据给你的整数,算出这个数的阶乘的位数。
输入
含若干行整数。第一行为n,表示案例数,接下来是n行,每行一个整数m(1≤m≤10^7)。
输出
输出这些数的阶乘的位数。
样例输入 Copy
2
10
20
样例输出 Copy
7
19

补充一个公式:斯特林公式
斯特林公式是一条用来取n阶乘近似值的数学公式。一般来说,当n很大的时候,n阶乘的计算量十分大,所以斯特林公式十分好用,而且,即使在n很小的时候,斯特灵公式的取值已经十分准确。
斯特林公式为:
在这里插入图片描述
结合数学推导,求数字的位数可借助lgx函数,则求阶乘的位数即对n!求以10为底的对数,根据对数的运算法则(对数相加减,真数相乘除),等号左边为从1到n各个数的以10为底的对数值,此时若用循环累加的话很可能会超时,因此采用斯特林公式。等号右边为lg(2*pi*m)/2+m*lg(m/e),x=1时单独验证,x大于1时,可根据此等式求解。

#include<stdio.h>
#include<math.h>
int main()
{
    int n;
    int m;
    double sum;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d",&m);
        if(m==1)printf("1");
        else
        {
            sum=log10(2*acos(-1)*m)/2.0+m*log10(m/exp(1));//pi用acos(-1)表示,
            printf("%d",(int)(sum+1));                    //自然对数e用exp(1)表示
        }
        if(n)printf("\n");
    }
    return 0;
}

3.求阶乘末尾0的个数
参考资料:
1.计算n阶乘中尾部零的个数
2.判断N!阶乘中末尾0的个数
4.第3条的变式训练——求n!!的末尾0的个数
问题 L: Double Factorial
时间限制: 1 Sec 内存限制: 128 MB
[提交] [状态]
题目描述
For an integer n not less than 0, let us define f(n) as follows:
·f(n)=1 (if n<2)
·f(n)=nf(n−2) (if n≥2)
Given is an integer N. Find the number of trailing zeros in the decimal notation of f(N).
Constraints
·0≤N≤10^18
输入
Input is given from Standard Input in the following format:

N

输出
Print the number of trailing zeros in the decimal notation of f(N).
样例输入 Copy
【样例1】

12

【样例2】

5

【样例3】

1000000000000000000

样例输出 Copy
【样例1】

1

【样例2】

0

【样例3】

124999999999999995

提示
样例1解释
f(12)=12×10×8×6×4×2=46080, which has one trailing zero.
样例2解释
f(5)=5×3×1=15, which has no trailing zeros.
题目大意是求n!!末尾0的个数,由于n的奇偶性不同,需要分类讨论。
n为奇数时,f(n)=n*(n-2)*(n-4)...5*3*1,for example,取n=15,则f(15)=15*13*11*9*7*5*3*1。n为偶数时,f(n)=n*(n-2)*(n-4)...6*4*2,for example,取n=16,则f(16)=16*14*12*10*8*6*4*2。由于2*5=10,因此n为奇数时,f(n)含有5,但一定不含2,此时末尾0的个数为0;n为偶数时,显然分解因数后2的个数要多于5的个数,下面就来求整个式子中含有5的个数。
显然2和5只能同时出现在10,20,30,40,50,…这种数列中,下面列举从10到400的情况:

         n: 10  20  30  40  50
含有5的个数:  1   1   1   1   2
         n: 60  70  80  90 100
含有5的个数:  1   1   1   1   2
         n:110 120 130 140 150
含有5的个数:  1   1   1   1   2
         n:160 170 180 190 200
含有5的个数:  1   1   1   1   2
         n:210 220 230 240 250
含有5的个数:  1   1   1   1   3
         n:260 270 280 290 300
含有5的个数:  1   1   1   1   2
         n:310 320 330 340 350
含有5的个数:  1   1   1   1   2
         n:360 370 380 390 400
含有5的个数:  1   1   1   1   2

思路大体就能得出:首先除以10,得到的数表示含有多少个“10”,就相当于含有多少个5,然后变成:

第几个"10": 1  2  3  4  5
含有5的个数: 0  0  0  0  1
第几个"10": 6  7  8  9 10
含有5的个数: 0  0  0  0  1
第几个"10":11 12 13 14 15
含有5的个数: 0  0  0  0  1
第几个"10":16 17 18 19 20
含有5的个数: 0  0  0  0  1
第几个"10":21 22 23 24 25
含有5的个数: 0  0  0  0  2
第几个"10":26 27 28 29 30
含有5的个数: 0  0  0  0  1
第几个"10":31 32 33 34 35
含有5的个数: 0  0  0  0  1
第几个"10":36 37 38 39 40
含有5的个数: 0  0  0  0  1

在上一步的基础上逐次除以5(每隔5位含有"5"),并把做除法后的结果累加,直到除以5后的结果为0为止。
举个例子,取n=296,首先n/10得到29,把29累加,然后用29除以5,得到5,累加5,用5除以5,得到1,累加1,用1除以5,得到0,结束,累加结果为35,经检验正确。这一算法思路就出来了,而且时间复杂度为O(logN),不超时。

#include<stdio.h>
typedef long long int lli;
int main()
{
    lli n;
    scanf("%lld",&n);
    lli f=n/10;
    lli sum=0;
    if(n%2==1)printf("0");
    else
    {
        while(f)//就是上面分析的思路
        {
            sum+=f;
            f=f/5;
        }
        printf("%lld",sum);
    }
    return 0;
}
/**************************************************************
    Language: C++
    Result: 正确
    Time:1 ms
    Memory:1120 kb
****************************************************************/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值