以前遇到数论题直接懵逼,今天开始好好搞搞基础的数论知识。
一下内容证明我可能会省略,毕竟我太弱了….
.
.
.
.
.
几个常用的定律:
( a + b ) mod p = ( a mod p + b mod p ) mod p
( a * b ) mod p = ( (a mod p) * (b mod p) ) mod p
c * ( a mod p ) = ( c *a ) mod ( c *b ) ————————条件:(c*y!=0)
.
.
.
.
其实就是求最大公约数的辗转相除法
以下是抄来的证明,毕竟我太菜………..
.
.
.
.
首先考虑一下:
对于任意两个正整数 a,b ,都有:
a=kb+r (k,r∈N)
所以有:
r=a%b (在这里,%指的是取余运算)
然后我们假设 c 是 a 和 b 的最大公约数,即
c=gcd(a,b)
然后,我们就能得到:
c|a c|b (x|y 表示 x 能够整除 y , y能被x整除 , 也就是y/x是整数)
然后又因为上面那个式子,有:
r=a−kb
所以有:
c|r
那么我们就可以知道,既然a和b的因数也是b和(a%b)的因数,那么它们的最大公因数肯定也是相同的。
整合一下上面的式子,我们可以得到:
c=gcd(b,r)
即
gcd(a,b)=gcd(b,a%b) ----------gcd(a,b)表示a和b的最大公约数
而且
gcd(a,0) = a
.
.
.
.
辗转相除法函数代码:
int gcd(int a,int b)//就是欧几里得算法函数,即辗转相除法,求gcd(a,b)
{
int c;
while(b!=0)
{
c=a;
a=b;
b=c%b;
}
int ans=a;
return ans;
}
这里还有一个点:lim ( a , b ) * gcd ( a , b ) =a * b
————————-这里lim( a, b )表示a和b的最小公倍数
.
.
.
.
由于我实在是菜鸡,只能把别人的证明截屏下来
dalao的博客传送门:https://blog.sengxian.com/algorithms/gcd-extgcd
.
.
.
.
这里有一道例题:https://www.luogu.org/problemnew/show/P1082
题目就是求关于 x 的同余方程 ax ≡ 1 (mod b)的最小正整数解
可能乍一看,ax ≡ 1 (mod b)跟上面的ax+by=gcd(a,b)这一个方程不太一样啊,没事,让我们来推导一下。
.
.
首先,题目保证了b是素数,即gcd(a,b)一定是1
我们设r=a*x%b, 有a*x=b*k+r
然后ax ≡ 1 (mod b)就转换为了a*x-b*k=1
然后我们再设 y=-k,方程就转换成了 a*x+b*y=1
即a*x + b*y = gcd(a,b) = 1
就是妥妥的扩展欧几里得算法嘛!!! 递归求x的值就好啦!!!!
这里我还要提一下:我们递归求出来的x可能并不是最小正整数,还看是负数,我们这时候就需要处理一下。需要将x mod p,然后加上p(为了搞定负数),再mod p,代码就是:x = (x%p+p) % p;
#include
#include
#include
using namespace std;
long long x,y,n;//最好定全局变量
void exgcd(long long a,long long b)
{
if(b==0) //当b=0时就是遇到了特解,可以递归回去算答案了
{
x=1,y=0;
return ;
}
exgcd(b,a%b);
long long k;
k=x;
x=y;
y=k-(a/b)*y;
}
int main()
{
long long a,p;
scanf("%d%lld",&a,&p);
exgcd(a,p);
cout<<(x%p+p)%p;
return 0;
}
以下内容有些摘抄自dalao博客,传送门:https://www.luogu.org/blog/zjp-shadow/cheng-fa-ni-yuan
算逆元的三个方法:
inline void exgcd(LL a,LL b)//扩展欧几里得算法求乘法逆元
{
if(b==0)
{
x=1,y=0;
return ;
}
exgcd(b,a%b);
LL k;
k=x;
x=y;
y=k-(a/b)*y;
}
.
.
.
.
int quick(int x,int p)//快速幂求乘法逆元,谨记,p是一个素数
{
int ans=1;
int d=p-2;
while(d)
{
if(d%2==1)
{
ans*=x;
ans%=p;
}
x*=x;
x%=p;
d/=2;
}
return ans;
}
这里有一个模板题目,就是洛谷的 P3811 【模板】乘法逆元
题目传送门:https://www.luogu.org/problemnew/show/P3811
#include
#include
#include
using namespace std;
long long x,y,n,f[3000010];
void work(long long n,long long p)//线性求逆元,时间复杂度O(n)
{
f[1]=1;
for(long long i=2;i<=n;i++)
{
f[i]=-(p/i)*f[p%i];
f[i]=(f[i]%p+p)%p;
}
}
int main()
{
long long a,p,b,n,i;
cin>>n>>p;
work(n,p);
for(long long i=1;i<=n;i++)
{
printf("%lld\n",f[i]);//处理出 最小正整数!!
}
return 0;
}