LCM性質 + 組合數 - HDU 5407 CRB and Candies

題目描述

給定一個數n,求LCM(C(n,0),C(n,1),C(n,2)...C(n,n))的值,(n<=1e6)。題目連接php

解題思路

頗有趣的一道數論題!ios

看了下網上別人的作法,什麼Kummer定理我還真沒據說過,仔細研究一下那個鬼定理真是漲姿式了!this

然而這題我並非用Kummer那貨搞的(what?).spa

其實這題真的很簡單(不要打我),爲何這樣說呢?看了下面的解釋你就知道我沒騙你。code

首先咱們看一下這個式子:LCM(C(n,0),C(n,1),C(n,2)...C(n,n))blog

當時個人第一感受是:暈,仍是打個表吧!結果,打表程序後臺打了四個半小時也沒打完=.=(時間複雜度算錯了)ci

作這題首先你得知道這個(基本常識):get

求多個數的最小公倍數,有兩種方法:string

1)分解質因數法it

先把這幾個數分解質因數,再把它們一切公有的質因數和其中幾個數公有的質因數以及每一個數的獨有的質因數所有連乘起來,所得的積就是它們的最小公倍數。

例如,求LCM[12,18,20,60]

由於12=(2)×[2]×[3],18=(2)×[3]×3,20=(2)×[2]×{5},60=(2)×[2]×[3]×{5}

其中四個數的公有的質因數爲2(小括號中的數),

三個數的公有的質因數爲2與3[中括號中的數],

兩個數的公有的質因數爲5{大括號中的數},

每一個數獨有的質因數爲3。

因此,[12,18,20,60]=2×2×3×3×5=180。

2)公式法

因爲兩個數的乘積等於這兩個數的最大公約數與最小公倍數的積

即(a,b)×[a,b]=a×b。

因此,求兩個數的最小公倍數,就能夠先求出它們的最大公約數,而後用上述公式求出它們的最小公倍數

例如,求[18,20]

即得[18,20]=18×20÷(18,20)=18×20÷2=180。

求幾個天然數的最小公倍數,能夠先求出其中兩個數的最小公倍數,

再求這個最小公倍數與第三個數的最小公倍數,依次求下去,直到最後一個爲止。

最後所得的那個最小公倍數,就是所求的幾個數的最小公倍數。

知道這個後,作這題選擇哪一種方法呢?

若是選擇第二種方法,恭喜你,你絕壁和我同樣想到打表滾粗!

既然第二種方法不行,確定只能是第一種方法了。

那麼要怎麼作呢?

首先咱們來看,對於組合數C(n,m),能夠有以下變換:

C(n,m)=n!/[(n-m)!*m!]=n*(n-1)*(n-2)*....(m+1) / (n-m)! 

這一步應該沒問題吧!

也就是:C(n,m)=n!/[(n-m)!*m!]=n*(n-1)*(n-2)*....(m+1) / (n-m)!  = n*(n-1)*(n-2)*....(m+1)/1/2/3/4/5/..../(n-m)

咱們把先後結合一下,邊乘邊除:

對於第k步,就至關於*(n+1-k)且/k,k={1,2,...n-m}.

咱們以n=8爲例:

C(8,0)=1

C(8,1)=8*7*6*5*4*3*2 /7/6/5/4/3/2/1

C(8,2)=8*7*6*5*4*3 /6/5/4/3/2/1

C(8,3)=8*7*6*5*4 /5/4/3/2/1

C(8,4)=8*7*6*5 /4/3/2/1

C(8,5)=8*7*6 /3/2/1

C(8,6)=8*7 /2/1

C(8,7)=8 /1

C(8,8)=1

結合求n個數的LCM的方法,咱們將問題轉換成:

找i個數共有的質數,而後相乘就可,i={1,2,..n}。

好了,你可能會說:*$#@*@,找i個數共有的質數難道不超時,並且你的代碼裏連一個0~n的for循環都沒有,你在逗我?

不急,看下面:

首先咱們明確一點,C(n,k)的最大質因數是不會大於n的。

那麼對於一個質數p來講,他對"n個數的LCM"的貢獻在哪?

是否是就是p^1,p^2,p^3...中的一些?

哪些呢?

前面求組合數中,咱們把C(n,m)分紅了分子和分母來看。

若是p^x可以整除(n-1+k),那麼他有多是知足的,可是還不夠,還要看是否是會被分母抵消掉。

只有p^x知足(n-1+k)%(p^x)==0且知足k%(p^x)!=0,這個p^x纔是知足的,也就是對答案纔有貢獻,此時ans須要乘以p。

最後一步,約約分可能會更方便:把分子分母合一下,變成了:(n-1)%(p^x)!=0,表示(n-1+k)%(p^x)==0和k%(p^x)!=0不是同時出現的,此時才知足。

OK,推導完畢。

最終方法就是:

先篩出1e6之內的全部素數p,而後判斷(n-1)%(p^x)是否!=0,是的話,ans*=p。

 

時間複雜度

O(p_num*sqrt(n))

代碼

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-08-21-15.17
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#define mod 1000000007
#define  LL long long
#define  ULL unsigned long long
using namespace std;
const int NN=1000010;
bool v[NN];
int p[NN],num;
void makePrime(){
   int i,j;
   num=-1;
   for(i=2; i<NN; ++i){
         if(!v[i]) p[++num]=i;
         for(j=0; j<=num && i*p[j]<NN; ++j){
               v[i*p[j]]=true;
               if(i%p[j]==0) break;
         }
   }
}
int main(){
   ios_base::sync_with_stdio(false);
   cin.tie(0);
   makePrime();
   int t;
   scanf("%d",&t);
   while(t--){
         int n;
         scanf("%d",&n);
         LL ans=1;
         for(int i=0; i<=num; ++i){
               for(LL t=p[i]; t<=n; t*=p[i]){
                     if((n+1)%t!=0)
                           ans=ans*p[i]%mod;
               }
         }
         printf("%lld\n",ans);
   }
   return 0;
}
相關文章
相關標籤/搜索