数论篇 卷一 未知的世界

我爱学习,学习使我快乐。

然而数学使我吐血。

本篇将会粗略整理一些基础数论知识,并汇总博主做过的一些数学知识要求高的题目。

但到目前为止,博主什么都不会,所以这里基本什么也没有。

一、欧几里得算法

 也就是俗称的辗转相除法。

甩个代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 int x,y;
 8 int gcd(int x,int y){
 9     return y?gcd(y,x%y):x;
10 }
11 int main(){
12     scanf("%d%d",&x,&y);
13     if(x<y)swap(x,y);
14     printf("%d",gcd(x,y));
15     return 0;
16 }
View Code

TYVJ3966有道裸题。刷题记录喜+1

TYVJ1727需要来一波理论分析。道理虽然懂了,但是具体实现需要一大堆高精写上去,我选择弃坑

二、扩展欧几里得算法

  对于不完全为 0 的非负整数 a,b,$gcd(a,b)$表示 a,b 的最大公约数。必然存在无数组整数对 (x,y) ,使得 $ gcd(a,b)=ax+by $ 。
1 int exgcd(int a,int b,int &x,int &y){
2     if(b==0){
3         x=1;y=0;
4         return a;
5     }
6     int res=exgcd(a,a%b,y,x);
7     y-=a/b*x;
8     return res;
9 }
1 void exgcd(int a,int b,int &x,int &y){
2     if(b==0){
3         x=1;y=0;
4         return;
5     }
6     exgcd(b,a%b,x,y);
7     int t=x;x=y;y=t-a/b*y;
8 }

三、线性筛

   这篇讲得特别好:

  http://blog.csdn.net/dinosoft/article/details/5829550

四、欧拉函数

  欧拉函数是小于等于n的数中与n互质的数的数目。

  设n为正整数,以 φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值,这里函数φ:N→N,n→φ(n)称为欧拉函数。
  欧拉函数是积性函数——若m,n互质, 
  当n为奇数时,, 证明与上述类似。
  若n为质数则 
 
  计算通式:
       
      其中p1, p2……pn为x的所有质因数,x是不为0的整数。
      (注意:每个质因数只计算一次)
  
 
  C++中的计算模板:(学自百度百科)
    (第20行的那个式子还没怎么弄懂)
  
 1 /*by SilverN*/
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 using namespace std;
 8 const int mxn=10000;
 9 int phi[mxn];//欧拉函数数组 
10 int mark[mxn];//[i]的最小质因数 
11 int pr[mxn],cnt=0;//质数数组 
12 void euler(){
13     phi[1]=1;
14     int i,j;
15     for(i=2;i<mxn;i++){
16         if(!mark[i])pr[++cnt]=mark[i]=i,phi[i]=i-1;//素数性质,f(i)=i-1 
17         for(j=1;j<=cnt && pr[j]*i<mxn;j++){
18             mark[i*pr[j]]=pr[j];
19             if(mark[i]==pr[j]){
20                 phi[i*pr[j]]=pr[j]*phi[i];
21                 ///*这里的phi[k]与phi[i]后面的∏(p[i]-1)/p[i]都一样(m[i]==p[j])只差一个p[j],就可以保证∏(p[i]-1)/p[i]前面也一样了*/
22                 break;
23             }
24             else phi[i*pr[j]]=phi[i]*(pr[j]-1);
25             //积性函数性质,f(i*k)=f(i)*f(k)
26             }    
27         }
28     return;
29 }
30 int main(){
31     euler();
32     int n;
33     scanf("%d",&n);
34     printf("%d
",phi[n]);
35     return 0;
36 }
View Code

   也可以直接按照上面的通式计算:

 1 long long phi(long long x){
 2     long long a=x;
 3     for(long long i=2;i<=m;i++){
 4         if(x%i==0){//找到因数 
 5             a=a/i*(i-1);//基本计算公式 a*=((i-1)/i) 
 6             while(x%i==0)x/=i;//除去所有相同因数 
 7         }
 8     }
 9     if(x>1)a=a/x*(x-1);//处理最后一个大因数 
10     return a;
11 }
View Code

  一道裸题:Bzoj2705

  题解链接附:http://www.cnblogs.com/SilverNebula/p/5656723.html

五、中国剩余定理

 中国剩余定理用来解决如下的同余方程的解 

  $ x equiv b_1 (mod m_1)$
  $ x equiv b_2 (mod m_2) $
  $ x equiv b_3 (mod m_3) $

  ...

若$m_1 ,m_2 ,m_3 , ...$ 互素,则方程有整数解。

设$M = m_1 * m_2 *m_3 ...$ ,在模M意义下同余方程组有唯一整数解。

$ ans=sum_{i=1}^{k} b_i * frac{M}{m_i}*inv(frac {M}{m_i},m_i) mod M $

我们知道所有的m两两互质,所以 $frac{M}{m_i}$ 是除$ m_i $以外所有的m的最小公倍数。
设有一个 $ c_i $ 满足
  $ c_i equiv 0 (mod frac{M}{m_i})$ ,
  $ c_i equiv 1 (mod m_i)$
显然有
  $ b_i *c_i equiv 0 (mod frac{M}{m_i})$
  $ b_i *c_i equiv b_i (mod m_i)$
现在来求c:
  $ frac{M}{m_i} * x = c_i=m_i*y+1$
也就是
  $ frac{M}{m_i} * x - m_i*y =1$
这个东西可以用扩展欧几里得解出来。
解该方程也可以看作是求 $frac{M}{m_i}$ 在模 $m_i$ 意义下的逆元。

我们把解出的这个x乘上 frac{M}{m_i},再乘上$ b_i $ ,加到ans里,现在ans满足了当前这个同余方程,并且其他同余方程的余数没有受到影响。

如此处理每一个同余方程,就得到了方程组的解。

 1 LL exgcd(LL a,LL b,LL &x,LL &y){
 2     if(!b){
 3         x=1;y=0;
 4         return a;
 5     }
 6     LL res=exgcd(b,a%b,x,y);
 7     LL t=x;x=y;y=t-a/b*x;
 8     return res;
 9 }
10 LL CRT(){
11     LL M=1;
12     LL res=0;
13     int i,j;LL x,y;
14     for(i=1;i<=n;i++){M*=a[i];}
15     for(i=1;i<=n;i++){
16         LL tmp=M/a[i];
17         exgcd(tmp,a[i],x,y);
18         res=(res+tmp*x*b[i])%M;
19     }
20     res=(res+M)%M;
21     return res;
22 }
CRT

六、扩展中国剩余定理

用来解决模数并不互质的同余方程组

其实没有这个定理(大概),所谓“扩展”就是用扩展欧几里得把模数不互质的同余方程组化成模数互质的同余方程组,再用中国剩余定理求解。

例如
$ x equiv c_1 (mod m_1) $
$ x equiv c_2 (mod m_2) $
可得
$m_1* k_1+c_1 = m_2 * k_2 + c_2$
$m_1 * k_1 - m_2 * k_2 = c_2-c_1$
根据裴蜀定理(http://www.cnblogs.com/SilverNebula/p/6807569.html)
该式有解的条件是$gcd(m_1,m_2)|(c_2-c_1)$
记$ GCD=gcd(m_1,m_2) $
此时可以将原式两边同除以GCD,得到:


$frac {m_1}{GCD} *k_1= frac{c_2-c_1}{GCD}+ frac{m_2}{GCD} * k_2$

$ k_1 * frac{m_1}{GCD} equiv frac{c_2-c_1}{GCD} (mod frac{m_2}{GCD})$

$ k_1 = inv(frac{m_1}{GCD},frac{m_2}{GCD}) * frac{c_2-c_1}{GCD}+y*frac{m_2}{GCD}$

将$k_1$代回刚开始的同余方程,得到


$x=m_1 * frac{c_2-c_1}{GCD} *inv(frac{m_1}{GCD} , frac{m_2}{GCD}) +c_1 + y* (m1*frac{m_2}{GCD})$

$x equiv m_1 * frac{c_2-c_1}{GCD} *inv(frac{m_1}{GCD} , frac{m_2}{GCD}) + c_1 (mod frac{m_1*m_2}{GCD})$

设$ C= m_1 * frac{c_2-c_1}{GCD} *inv(frac{m_1}{GCD} , frac{m_2}{GCD}) + c_1$
$ M= frac{m_1*m_2}{GCD}$


于是就把两个方程合并成了$x equiv C (mod M)$

把所有方程像这样依次合并起来,最后得到的C就是x的解

 1     LL m1=m[1],c1=c[1];
 2     for(i=2;i<=k;i++){
 3         LL m2=m[i],c2=c[i];
 4         LL tmp=gcd(m1,m2);
 5         if((c2-c1)%tmp){printf("NO
");return 0;}//无解
 6         LL m3=m1/tmp*m2,c3=inv(m1/tmp,m2/tmp);
 7         c3=ksmul(c3,(c2-c1)/tmp,m2/tmp);
 8         c3=ksmul(c3,m1,m3);
 9         c3=((c3+c1)%m3+m3)%m3;
10         m1=m3;c1=c3;
11     }
EX-CRT

CodeForces 338D GCD Table

七、Lucas定理

求解模意义下的组合数问题:

$C_{n}^{m} = prod_{i=1}^{k} C_{n_i}^{m_i} (mod p)$ 其中p是质数

$n=n_k * p^k + n_{k-1} + p^{k-1} + n_{k-2} * p^{k-2} + ... +n_1 * p + n_0$

$m=m_k * p^k + m_{k-1} + p^{k-1} + m_{k-2} * p^{k-2} + ... +m_1 * p + m_0$

也就是把n和m进行p进制拆分,然后分别计算。

傻傻不会证明

 1 int pw[mxn],inv[mxn];
 2 void init(){
 3     pw[1]=1;inv[1]=1;
 4     pw[0]=1;inv[0]=1;
 5     for(int i=2;i<mod;i++){
 6         pw[i]=pw[i-1]*i%mod;
 7         inv[i]=(-(mod/i)*inv[mod%i]%mod+mod)%mod;
 8     }
 9     return;
10 }
11 int calc(int n,int m){
12     if(n<m)return 0;
13     return pw[n]*inv[pw[m]]%mod*inv[pw[n-m]]%mod;
14 }
15 int lucas(int n,int m){
16     if(!m)return 1;
17     return calc(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
18 }
Lucas定理

八、扩展Lucas定理

参考了这里http://blog.csdn.net/clove_unique/article/details/54571216

$ C_n^m (mod P)$的值,P不一定是质数

把P分解质因数, $ P=p_1^{k_1} * p_2^{k_2} *p_3^{k_3} *...$

分别在模$ p_i^{k_i} $的意义下求出解,然后列出同余方程:
$x equiv c_1 (mod p_1^{k_1})$
$x equiv c_2 (mod p_2^{k_2})$
$x equiv c_3 (mod p_3^{k_3})$
$ ... $

显然这些$ p_i^{k_i} $ 两两互质,可以用中国剩余定理合并

那么$ C_n^m (mod p_i^{k_i})$ 怎么求?

$ C_n^m (mod p_i^{k_i})$
可以用组合数公式 $ C_n^m=frac{n!}{m!(n-m)!} $计算,此时我们需要快速求出 $n! mod p_i^{k_i} $
举个栗子:
$p=3 $, $k=2 $, $ p_i^{k_i}$=9 , $ n=19 $
$n!= 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19$
$n!= 1*2*4*5*7*8*10*11*13*14*16*17*19*3*6*9*12*15*18$
$n!= 1*2*4*5*7*8*10*11*13*14*16*17*19*3^6*1*2*3*4*5*6$
$n!= [1*2*4*5*7*8]*[(1+9)*(2+9)*(4+9)*(5+9)*(7+9)*(8+9)]*[19]*[3^6]*[1*2*3*4*5*6]$
第一部分可以通过求模p意义下的阶乘得到;

第二部分展开以后约掉带$p^k$的项就和第一部分一样;

第三部分是序列长度除以$p^k$多出来的部分,不超过$p^k$个,可以直接求;

第四部分是$p^{lfloor n/p floor}$;第五部分是连续的阶乘,可以递归计算。

注意到第四部分可能使得$ m! $ 和 $ (n-m)!$不与$p^k$互质,导致无法求逆。解决办法是不算它们,改算$ C_n^m $里共有多少个因数p,算完其他部分以后再把这些p乘进去
计算n!中质因子p的个数x的公式为$x=lfloor frac{n}{p} floor+lfloor frac{n}{p^2} floor +lfloor frac{n}{p^3} floor + ... $,可以递归计算。

原文地址:https://www.cnblogs.com/SilverNebula/p/5656506.html