[关闭]
@wzq 2017-08-17T02:33:57.000000Z 字数 4857 阅读 1599

数论相关

最大公约数和最小公倍数

最大公约数gcd

  1. int gcd(int a,int b)
  2. {
  3. if (b==0) return a;
  4. return gcd(b,a%b);
  5. }

最小公倍数lcm

  1. int lcm(int a,int b)
  2. {
  3. return a/gcd(a,b)*b;
  4. }

质数相关结论

1.一个正整数n,最多只有一个大于根号n的质因数。(基本应用是质因数分解)
2.一个10^9次方以内的数,最多有9个不同的质因子。(这个结论往往和容斥原理结合使用)

3.素数定理

表示不大于x的素数个数

质数筛选法

埃拉托斯特尼筛法

  1. const int maxm=(1e7)+5;
  2. int Prime[maxm];
  3. void Prepare_Prime()
  4. {
  5. static bool isPrime[maxm];
  6. memset(isPrime,true,sizeof(isPrime));
  7. Prime[0]=0; // Prime[0]纪录有多少个质数
  8. for (int i=2; i<maxm; i++)
  9. if (isPrime[i])
  10. {
  11. Prime[++Prime[0]]=i;
  12. for (int j=i+i; j<maxm; j+=i) isPrime[j]=false;
  13. }
  14. printf("%d\n",Prime[0]);
  15. }

欧拉筛选法

对[1,n]的数进行欧拉筛。

fp[i]记录i的最小质因数。(可用于质因数分解,也可判断i是否为质数)

pr[i]是质数数组,其中pr[0]为质数个数。

mu[i]为i的莫比乌斯函数,mus[i]为其前缀和。(莫比乌斯函数可以用来容斥和反演)

phi[i]为i的欧拉函数。

  1. const int maxn=1e7+100;
  2. int fp[maxn],pr[maxn],mu[maxn],phi[maxn],mus[maxn];
  3. void oula_prime(int n)
  4. {
  5. memset(fp,0,sizeof(fp));
  6. mu[1]=1;
  7. pr[0]=0;
  8. for (int i=2; i<=n; ++i)
  9. {
  10. if (!fp[i]) { pr[++pr[0]]=i,fp[i]=i,mu[i]=-1,phi[i]=i-1; }
  11. for (int j=1; j<=pr[0]; ++j)
  12. {
  13. int k=i*pr[j];
  14. if (k>n) break;
  15. fp[k]=pr[j];
  16. if (i%pr[j]==0)
  17. {
  18. mu[k]=0,phi[k]=phi[i]*pr[j];
  19. break;
  20. }
  21. else mu[k]=-mu[i],phi[k]=phi[i]*(pr[j]-1);
  22. }
  23. }
  24. mus[0]=0;
  25. for (int i=1; i<=n; ++i) mus[i]=mus[i-1]+mu[i];
  26. }

质因数分解

将一个正整数n,分解成(为不同质数)​的形式。

预处理试除法

一个正整数n,最多只有一个大于根号n的质因数。因此,枚举根号n以内的质数分解质因数。(以下代码质数预处理省略)

  1. struct P_factor
  2. {
  3. int p,k;
  4. P_factor() { p=k=0; }
  5. P_factor(int a,int b) { p=a,k=b; }
  6. };
  7. vector<P_factor> divide_factor(int x)
  8. {
  9. vector<P_factor> R;
  10. R.clear();
  11. for (int i=1; i<=Prime[0]; i++)
  12. {
  13. int p=Prime[i];
  14. if (p*p>x) break;
  15. if (x%p==0)
  16. {
  17. int k=0;
  18. while(x%p==0) k++,x/=p;
  19. R.push_back(P_factor(p,k));
  20. }
  21. }
  22. if (x>1) R.push_back(P_factor(x,1));
  23. return R;
  24. }

质因数分解形式下的gcd和lcm





推广到多个数,也是如此。

同余

定义和性质

定义:
给定正整数M,用M去除两个整数a和b,若余数相同,则称a和b对模M同余,记作

性质:
自反性:
对称性: 若, 则
传递性: 若, , 则

加减乘运算:
,c为任意整数

则有



除法运算:
,c为非0整数并且gcd(c,M)=1
则有
其中,inv[c]是c对模M的逆元,满足
推导如下:



还有一些性质可以自己找资料和推导

扩展欧几里得

上述谈到的最大公约数算法是数学家欧几里德提出的,同时,他也提出了扩展欧几里德算法来解决整数二元一次不定方程问题。

整数二元一次不定方程

形如a*x+b*y=c(a,b均不为0)的方程,a,b,c都是整数,求(x,y)的整数解。

1 判断是否有解

整数二元一次不定方程有解的充分必要是gcd(a,b)|c。如果不能整除则无解。

2 扩展欧几里德求特解

欧几里德给出了计算a*x+b*y=gcd(a,b)的解法

  1. long long exgcd(long long a,long long b,long long &x,long long &y)
  2. {
  3. if (b==0) { x=1,y=0; return a; }
  4. long long d=exgcd(b,a%b,x,y);
  5. long long tmp=x;
  6. x=y;
  7. y=tmp-a/b*y;
  8. return d;
  9. }

3 通解

a*x+b*y=gcd(a,b)在有解并求出特解(x,y)的情况下,通解可以表示为


对于一般式a*x+b*y=c,如果有解,只需把a*x+b*y=c的特解乘上c/gcd(a,b)即可得到其特解,通解还是一样的公式。

4 例子

求出特解和一个通解

  1. int main(int argc, char* argv[])
  2. {
  3. long long a,b,c;
  4. scanf("%lld%lld%lld",&a,&b,&c);
  5. long long x,y,d;
  6. d=exgcd(a,b,x,y);
  7. if (c%d!=0) printf("No solution!\n");
  8. else
  9. {
  10. a/=d,b/=d,c/=d;
  11. x*=c,y*=c;
  12. printf("特解: %lld %lld\n",x,y);
  13. printf("一个通解: %lld %lld\n",x+b,y-a);
  14. }
  15. return 0;
  16. }

下面两个程序,找的解满足x或y是第一个非负整数的解。

  1. void minimal_x(long long &x,long long &y,long long &a,long long &b,long long &c)
  2. {
  3. long long m=(b>0 ? b : -b);
  4. x%=m;
  5. if (x<0) x+=m;
  6. y=(c-a*x)/b;
  7. }
  8. void minimal_y(long long &x,long long &y,long long &a,long long &b,long long &c)
  9. {
  10. long long m=(a>0 ? a : -a);
  11. y%=m;
  12. if (y<0) y+=m;
  13. x=(c-b*y)/a;
  14. }

请思考一下,如何求出不定方程的正整数解个数。

欧拉函数

费马小定理

对于一个质数p和一个整数a,如果a和p互质,那么就有。该定理先由费马猜想,后由欧拉证明。欧拉证明的同时提出了欧拉函数。

定义

给定一个正整数n,定义等于1-n中与n互质的数的个数。对于任何整数a和n,如果a和n互质,那么就有

通式

不完全积性

在正整数定义域,若a和b互质,且有f(a*b)=f(a)*f(b)则称f(n)是一个积性函数(或不完全积性函数)。若对任意a,b,均有f(a*b)=f(a)*f(b),则称f(n)是一个完全积性函数。
常见的不完全积性函数就有欧拉函数

若有则有

筛法求欧拉函数

对于一个n,我们可以用质因数分解和通式求欧拉函数。然而如果有多个欧拉函数,并且n值都不大的情况下,这样的做法效率不高。利用欧拉函数的不完全积性,我们可以用筛选法O(n)求出1-n的欧拉函数。
在看筛法之前,我们先看几条常见的公式(以下所有字母表示的都是正整数,p是质数):

  1. ,则


代码参考欧拉筛选法求质数

逆元

定义

如果,那么a和b互为对模M的逆元,b可以记为,反之亦然(以下记为inv[a])。
在同余部分提到过,a/b mod M 相当于a*inv[b] mod M。

逆元的求法

扩展欧几里得法

可化为等式

把a当成x,就相当于解二元一次不定方程。

欧拉函数法

如果a和M不互质,那么就不存在a对模M的逆元。
如果a和M互质,那么就是a对模M的一个逆元,特别地,当M为质数时,就是a对模M的一个逆元。

因此,求出欧拉函数或当M为质数时,我们可以用快速幂乘法在log级别的时间复杂度内求出a对模M的逆元。

除此之外,还有一种预处理的方式求1-n对模M的逆元。

O(n)预处理法

如果M为质数,那么显然1到M-1都有对模M的逆元。假设,那么就可以O(n)预处理出1-n对模M的逆元。

  1. const int maxn=(1e6)+100;
  2. int inv[maxn];
  3. void Prepare_inv(int n,int M)
  4. {
  5. inv[1]=1; //1 的逆元是1
  6. for (int i=2; i<=n; i++)
  7. inv[i]=(long long)(M-M/i)*inv[M%i] % M;
  8. }


<=>
模等号两边同时乘上r的逆元
<=>
<=>

<=>

由逆元定义可得,就是i的逆元。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注