M.J的blog

algorithm,ACM-ICPC
随笔 - 39, 文章 - 11, 评论 - 20, 引用 - 0
数据加载中……

求N的阶乘约数的个数

先说一个定理:

        若正整数n可分解为p1^a1*p1^a2*...*pk^ak
        其中pi为两两不同的素数,ai为对应指数
        n的约数个数为(1+a1)*(1+a2)*....*(1+ak)
        如180=2*2*3*3*5=2^2*3^2*5
       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*3^8*5^4*...*19^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的阶。其它可以依次递推。

所以在求N的阶乘质数因数个数时,从最小的质数开始,

1int cal(int n, int p) {
2     if(n < p) return 0;
3     else return n / p + cal(n / p, p);
4}

其中P是质数,则该函数返回的就是N的阶乘中可以表达成质数P的指数的最大值。原理如上。

下面附上TOJ 2308的AC代码:


#include<iostream>
#include
<cmath>
using namespace std;
#define N 90
#define M 450  
int p[M+2]={0};
int prime[N+2],l,q,t=1;          //求前90个素数
void getprime(int n)
{
   
for(l=2;l<n;l++)
   
{
       
if(!p[l])
       
{
            
for(q=l+l;q<n;q+=l)
            
{
                p[q]
=1;
            }

            prime[t]
=l;t++;
       }

   }

}

int cal(int n,int m)   //求N的阶乘含质因数M的次数
{
   
if(m>n)
      
return 0;
   
else
      
return n/m+cal(n/m,m);
}

int main()
{
   
int i,j,k,n;
   
long long m;
   getprime(M);
   
while(cin>>n>>k)
   
{
      
if(2*k>n)  k=n-k;
      
for(i=1,m=1;prime[i]<=n,i<t;i++)
          m
*=(cal(n,prime[i])-cal(k,prime[i])-          cal(n-k,prime[i])+1);  
      cout
<<m<<endl;
   
   }

}

posted on 2010-04-21 23:04 M.J 阅读(2977) 评论(0)  编辑 收藏 引用 所属分类: ACM-ICPC


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理