数论

数论

最大公因数GCD

推荐阅读

辗转相除法

略。

Stein

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//Stein
gcd(a,b)
{    
    if(a==b)
    {
        gcd=a;
        return;
    }
    if(a为偶数b为奇数)//一定没有2因子
        gcd=gcd(a/2,b);
    if(a为奇数b为偶数)//同上
        gcd=gcd(a,b/2);
    if(a,b均为偶数)
        gcd=2*gcd(a/2,b/2);//一定有2因子
    if(a,b均为奇数)
    {
        if(a<b)    gcd=gcd(b,a);
        gcd=gcd(a-b,b);
    }
}

斐波那契数列最大公因数的性质

gcd(fn,fm)=f(gcd(n,m))

逆元

强烈推荐:http://www.cnblogs.com/linyujun/p/5194184.html

概念

定义

对于互质的a,m∈N,称满足下式的最小x∈N为a在模m意义下的逆元(或数论倒数)。

$$ ax\equiv1\pmod{m} $$

在代码中,由于逆元的英文为Inverse element,常常用inv(a)表示a的逆元(模数m一般定义为全局变量)

性质

集合A{a|a=1,2,3…p}​,B{b|b=inv(a),a∈A},则A=B。

换句话说:1~p所有数的逆元都不重复。

求法

费马小定理

费马小定理:p为质数时,$a^{p-1}\equiv1\pmod{p}$

则:$a^{p-2}\equiv inv(a)\pmod{p}​$

而$a^{p-2}$可以用快速幂(边算边模)求出。

扩展欧几里得算法

不定方程ax+by=1可以用扩展欧几里得算法求出一组解,而有解的条件是gcd(a,b)=1,即a和b互质。将等式左右两边同模b,可得:

$$ ax\equiv1\pmod{b} $$

因此求得的不定方程解x也同时是a在模b意义下的逆元。

同理y是b在模a意义下的逆元。

递推

设$x=p \% a,y=⌊\frac{p}{a}⌋$

有:

$$ \begin{aligned} x+ya&=p\\ (x+ya)&\equiv0\pmod{p}\\ x&\equiv -ya \pmod{p}\\ x*\mathrm{inv}[a]&≡(-y) \pmod{p}\\ \mathrm{inv}[a]&≡(p-y)*\mathrm{inv}[x] \pmod{p}\\ \mathrm{inv}[a]&≡(p-⌊\frac{p}{a}⌋)*\mathrm{inv}[p \% a] \pmod{p} \end{aligned} $$

由于p % a< a,所以在代码中就可以这样递推求出1~p所有数的逆元:

1
2
3
4
5
6
void initInv()
{
    inv[1]=1;
    for(int i=2;i<=p;++i)
        inv[i]=1ll*(p-p/i)*inv(p%i)%p;
}

其中等号左边乘上1ll是由于在乘法计算过程中有可能会超出int范围,但是结果一般不会超出,在左边乘上1ll可以将右边的计算转换为long long 类型。

当然,在程序中全部使用long long 一定是没有问题的。

应用

求ans=(a/b) mod m

解法一:逆元 Ans=a*inv(b) mod b

解法二:若已知b|a,则ans=(a mod mb)/b

证明:

$$ \begin{aligned} 设a/b&=km+x\ a&=kmb+bx\ a % bm&=bx\ (a % bm)/b&=x\ \end{aligned}

$$

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
乘法逆元模板
*/
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
template<typename T>
void write(T w)
{
	if(w<0)putchar('-'),w=-w;
	if(w>9)write(w/10);
	putchar(w%10+48);
}
const int maxn=int(3*1e6+3);
typedef long long lint;//否则乘的时候会爆 
lint p,n;
lint inv[maxn];
int main()
{
	scanf("%lld%lld",&n,&p);
	inv[1]=1;
	puts("1");
	for(lint i=2;i<=n;++i)
	{
		inv[i]=(p-p/i)*inv[p%i]%p;
		write(inv[i]);
		putchar('\n');
	}
	return 0;
 } 

裴蜀定理

内容

a,b是整数,且gcd(a,b)=d,∀x,y∈Z,都有d|ax+by。 特别地,一定存在整数x,y,使ax+by=d成立。

推论

gcd(a,b)=1$\iff$存在整数x,y使ax+by=1:。

推广

设a1,a2,a3……an为n个整数,d是它们的最大公约数,存在整数x1……xn使得x1*a1+x2*a2+…xn*an=d。 特别地,如果a1…an互质(不是两两互质),那么存在整数x1……xn使得x1*a1+x2*a2+…xn*an=1。

相关性质

​ d=gcd(a,b),则ax+by的最小正值为d。(推广后仍成立)

线性筛

朴素线性筛

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
筛出1-n当中的质数,同时计算欧拉函数、莫比乌斯函数、约数个数
注意其中i*prime[j]<=n,否则可能越界
*/
/*
prime[]数组中的素数是递增的,当i能整除prime[j],
那么i*prime[j+1]这个合数肯定被prime[j]乘以某个数筛掉。
   因为i中含有prime[j],prime[j]比prime[j+1]小,即i=k*prime[j],
   那么i*prime[j+1]=(k*prime[j])*prime[j+1]=k’*prime[j],
   接下去的素数同理。所以不用筛下去了。
   即在第一重循环的时候i*prime[j+1]会被k'筛掉 
   因此,在满足i%prime[j]==0这个条件之前以及第一次
   满足改条件时,prime[j]必定是prime[j]*i的最小因子。
*/
flag[maxn],prime[maxn];//flag[i]表示i被筛过,i不是质数 

void kick()
{
	flag[1]=0;
	for(int i=2;i<=n;i++)
	{
		if(!flag[i]) 
			prime[++k]=i;
		for(int j=1;j<=k&&i*prime[j]<=n;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
}

线性筛欧拉函数

首先请自行了解一些关于积性函数的知识。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
phi[maxn];
void kick_phi()
{
	flag[1]=0;
	for(int i=2;i<=n;i++)
	{
		if(!flag[i])
		{
			prime[++k]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=k&&i*prime[j]<=n;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				/*
				解释1:
				φ(i)=i(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn) 
				if(i%p==0) φ(i*p)=(i*p) (1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn)
				或者说,p已经是{pi}中的一个
				解释2:
				i*p相当于p个长度为n的区间
				设x (1~i) 且gcd(x,i)=1
				那么对i*p中任意x+k*i
				有gcd(x+k*i,i)=gcd(i,(x+k*i)%i)=gcd(i,x)=1
				
				所以每个区间
				*/
				break;
				
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);//质数时 积性
		}
	}
}

线性筛莫比乌斯函数

莫比乌斯函数 定义域是N μ(1)=1 1. 当n存在平方因子时,μ(n)=0 2. 当n是素数或奇数个不同素数之积时,μ(n)=-1 3. 当n是偶数个不同素数之积时,μ(n)=1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void kick_mobious()
{
	flag[1]=0;
	for(int i=2;i<=n;i++)
	{
		if(!flag[i])
		{
			prime[++k]=i;
			m[i]=-1;//2.
		}
		for(int j=1;j<=k&&i*prime[j]<=n;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				m[i*prime[j]]=0;//1.
				break;
			}
			else m[i*prime[j]]=-m[i];//m[i*prime[j]]=m[i]*m[prime[j]]=m[i]*(-1)=-m[i]    (积性函数) 
		}
	}
}

线性筛约数个数

d[i]表示i的最小因数的次数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void kick_facnum()
{
	flag[1]=0;
	for(int i=2;i<=n;i++)
	{
		if(!flag[i])
		{
			prime[++k]=i;
			f[i]=2;
			d[i]=1;
		}
		for(int j=1;j<=k&&i*prime[j]<=n;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				f[i*prime[j]]=f[i]/(d[i]+1)*(d[i]+2);//at the moment d[i]就是prime[j]的次数 
				d[i*prime[j]]=d[i]+1;
				break;
			}
			else
			{
				f[i*prime[j]]=(f[i]<<1);//=f[i]*(1+1)=f[i]*2
				d[i*prime[j]]=1;
			}
		}
	}
}

题目

0%