1. 程式人生 > >數論----高效又實用的解題方法----洗牌機

數論----高效又實用的解題方法----洗牌機

一、數論的相關知識

1、整除

(1)、概念:設a是非零整數,b是整數。

如果有一個整數q,可以讓b=a*q,則a|b,也就是b是a的倍數,a是b的因數。

比如說:4|8,7|21   ........

(2)、性質:

1、若a|b,b|c,則a|c

2、若a|b,a|c,則a|b±c

3、若a|b,a|c,則a|(b*x+c*y)(x、y為整數)

4、若a|b,m≠0,則(m*a)|(m*b)

5、若a|n,b|n,且存在整數x,y使a*x+b*y=1,則(a*b)|n

6、若b=q*d+c,d|c,則d|b。(若b=q*d+c,d|b,則d|c)

這些性質看似用不到程式設計上,但是在一些題目中使用會大大降低程式設計的難度以及時間複雜度。

2、同餘

(1)、概念:設a,b均為整數,且a-b能被某個自然數m整除,則a≡b(mod m),也就是存在一個整數k使:a-b=m*k

(2)、性質:

1、a≡a (mod m)

2、若a≡b (mod m),則b≡a (mod m)

3、若a≡b (mod m),b≡c (mod m),則a≡c (mod m)

4、若a≡b (mod m),則a+c≡b+c (mod m)

5、a≡b (mod m),則a*c≡b*c (mod m)

6、a≡b (mod m),則a^c≡b^c (mod m)(a^b表示a的b次方)

7、分配律

(a±b)%m=(a%m±b%m)%m

(a*b)%m=(a%m*b%m)%m


除法不滿足分配律,但是滿足(a/b)%m=a%(b*n)/b

3、最大公因數與最小公倍數

(1)、概念:不用說了吧...........,a,b的最大公因數記為:GCD(a,b),a,b的最小公倍數記為:LCM(a,b)

當GCD(a,b)=1時,則a,b互質。

(2)、性質:

1、GCD(x,y)=GCD(x,y-x)=GCD(y,x%y)(輾轉相除法原理)

2、x*y=GCD(x,y)*LCM(x,y)   >>>>>   LCM(x,y)=x*y/GCD(x,y)

4、素數

(1)、概念:一個除1和它本身以外,沒有其他因數的數,1不是素數。

(2)、性質:

1、唯一分解定理

2、威爾遜定理:若p是素數,則(p-1)!≡-1(mod p)  (其中!表示階乘)(其逆定理也成立)

3、費馬定理:若p是素數,a是正整數,且a、p互質,則a^(p-1)≡1(mod p)  (其逆定理不成立

(3)、尤拉函式:φ(n)表示1~n之間與n互質的數。

(4)、尤拉定理:

1、若p為素數,則φ(p)=p-1

2、若p為素數,則φ(p^a)=(p-1)*p^(a-1)

3、若a,b互質,則φ(a*b)=φ(a)*φ(b)

4、若a,m互質,則a^φ(m)≡1 (mod m)

講了這麼多,來看一道例題吧

二、例題

洗牌機

題目描述

2n張牌,放在2n個從12n的有序位置上。洗牌機每次可以把第i張牌洗到pi)的位置上。Pi)的定義如下:


問經過最少多少輪洗牌,才會使所有牌回到原來的位置。

輸入

輸入格式:

有多組資料,每組資料一個整數n(n<=109)

輸出

輸出格式:

對於每組資料,輸出一個整數,表示答案。

樣例輸入

1

樣例輸出

2
、解題方法 1、我們先把p(i)的公式化成:p(i)=2*i%(2*n+1) 2、如果進行k次洗牌,則i的位置在2^k*i%(2*n+1) 3、為了運算簡便,我們把i取1,原式就轉化為:2^k%(2*n+1)

4、當p(i)再次等於1時,k就是答案,原問題就轉化為:2^k≡1 (mod 2*n+1),已知n,求最小的k。

5、因為2與2*n+1互質,由尤拉定理可知:2^φ(2n+1)≡1 (mod 2n+1),所以k=φ(2n+1)。

6、當n=3時,k=φ(2n+1)=6,但是k的最小值為3,洗牌過程如下。

1 2 3 4 5 6

4 1 5 2 6 3    (1)

2 4 6 1 3 5    (2)

1 2 3 4 5 6    (3)

7、我們可以找φ(2n+1)的因子q,如果q滿足2^q≡1 (mod 2n+1)時(通過快速冪演算法來計算),q就是k的最小值。

程式碼:

#include<cstdio>
#include<cmath>
long long m,n,x,ans;
long long P(long long x,long long y,int mod)
{
    long long ans=1;
    while(y){
        if(y&1) ans=(ans*x)%mod;
        y>>=1;x=x*x%mod;
    }
    return ans;
}
long long phi(long long n)
{
    long long m=(long long)sqrt(n+0.5),ans=n;
    for(int i=2;i<=m;i++)
        if(n%i==0){
            ans=ans/i*(i-1);
            while(n%i==0)n/=i;
        }
    if(n>1)
        ans=ans/n*(n-1);
    return ans;
}
int main()
{
    int i;
    while(~scanf("%lld",&n)){
        m=2*n+1;
        x=phi(m);
        ans=x;
        for(i=2;i*i<=x;i++){
            if(x%i==0){
                if(P(2,i,m)==1)
                    if(i<=ans){ans=i;break;}
                if(P(2,x/i,m)==1)
                    if(x/i<=ans){ans=x/i;}
            }
        }
        printf("%lld\n",ans);
    }
}