数论常用定理公式

裴蜀定理

若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立

费马小定理

假如p是质数,有a使得gcd(a,p)=1,有a^(p-1)≡1(mod p) (≡的意思是1和a^(p-1)mod p数一样)

中国剩余定理

 在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之 剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。

具体解法分三步

1.找出三个数:从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
2.用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加15∗2+21∗3+70∗2得到和233。
3.用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。:

威尔逊定理

若p为质数,则p可整除(p-1)!+1

欧拉定理

若n, a为正整数且互质,则 a^φ(n) ≡ 1 (mod n),φ(n)为1~n与n互质的个数

欧几里得算法

int gcd(int a, int b)
{
    if(b==0){
    	return a;
	}
	else return gcd(b,a%b);
}

扩展欧几里得定理

对于不完全为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然
存在整数对 x,y ,使得 gcd(a,b)=ax+by。

int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1;y=0;
		return a;
	}
	int d=exgcd(b,a%b,y,x);
	y=y-(a/b)*x;
	return d;
}

欧拉函数

欧拉函数是小于或等于n的正整数中与n互质的数的数目

int phi(int x)
{
    int res = x;
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    if (x > 1) res = res / x * (x - 1);

    return res;
}

并且欧拉函数满足积性函数性质(如果gcd(a,b)=1,f(a * b)=f(a) * f(b))
欧拉函数求法:设y分解质因数为n1,n2,n3.... 则f(y)=y * (1-1/n1) * (1-1/n2) * .....

最小公倍数与最大公约数的关系

lcm(a,b)=a*b/gcd(a,b)
lcm(a,b)常常这样计算:a/gcd(a,b) 由于是公约数必是整除,之后乘b即是lcm(a,b)

n*log(n)判断素数法

for(int i=2;i<=n;i++){
    is_prime[i]=1;
}
for(int i=2;i<=n;i++){
      for(int j=2*i;j<=n;j+=i)
            is_prime[i]=0;
}

O(n)判断素数法

#include<cstdio>
#include<iostream>
using namespace std;
int check[100000001];
int prime[1000001];
int main()
{
	int n,q;
	int cnt=0;
	//根据n<=10^8,q<=10^6开对应的数组大小,防止MLE
	//初始将check数组全部标记为0,标0的是素数,标1的不是素数
	scanf("%d%d",&n,&q);
	check[1]=1;//1不是素数
	for(int i=2;i<=n;i++)
	{
		if(!check[i])prime[cnt++]=i;//若当前数i没有被之前的所有数筛掉,表明i是素数,将i添加进素数表prime
		for(int j=0;j<cnt&&i*prime[j]<100000001;j++)//注意i*prime[j]不要超过n的上限(10^8)
		{
			check[i*prime[j]]=1;//将当前素数prime[j]的i倍标记为合数
			if(i%prime[j]==0)break;//关键步骤:保证每个合数只被筛一次
		}
	}
	for(int i=1;i<=q;i++)
	{
		scanf("%d",&n);
		printf("%d\n",prime[n-1]);//由于素数表从0开始存,所以输出时下标应减1
	}
	return 0;
}

posted @ 2020-10-14 21:39  一个经常掉线的人  阅读(655)  评论(0)    收藏  举报