(文章补充于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
****************************************************************/