数论 欧几里得算法
§ 欧几里得算法
基本知识
最大公约数、最小公倍数
用 gcd(a,b) 表示 \(a\) 和 \(b\) 的最大公约数
用 lcm(a,b) 表示 \(a\) 和 \(b\) 的最小公倍数
定理
lcm(a,b)=(a*b)/gcd(a,b)
最大公约数的相关性质
- \(\gcd(a,b)=\gcd(b,a)\)
- \(\gcd(a,b)=\gcd(-a,b)\)
- \(\gcd(a,b)=\gcd(b,a-b)\)
- \(\gcd(a,b)=\gcd(b,a\%b)\)
- \(\gcd(a,0)=|a|\)
- \(\gcd(a,ka) = |a|\)
- \(\gcd(ma,mb)=m\times\gcd(a,b)\)
- \(\gcd(a,b) = \gcd(a+mb,b)\)
- \(\gcd(ab,m)=\gcd(a,m)\times\gcd(b,m)\)
- \(\gcd(a,p)=1,\gcd(b,p)=1\Rightarrow\gcd(a\times b,p)=1\)
更相减损法(略)
Euclidean Algorithm
定义及原理
又称 辗转相除法
原理 \(\gcd(a,b)=\gcd(b,a\%b)\)
用法
template<class T>inline T gcd(T a,T b){
return b==0?a:gcd(b,a%b);
}
证明
裴蜀定理
定义及原理
又名贝祖定理
证明
扩展欧几里得
定义
已知 \(a,b\) ,扩展欧几里得算法可以求出 \(\gcd(a,b),以及x,y\in\Z\) ,满足 \(ax+by=\gcd(a,b)\)
用法
template<class T>inline T exgcd(T a,T b,T&x,T&y){
if(b==0){
x=1,y=0;return a;
}
T gcd=exgcd(b,a%b,y,x);
y=y-a/b*x;
return gcd;
}
解释
由上面的推导可以知道:
当递归达到边界条件时 (b==0) ,\(x=1,y=0\) 是目标方程的一组解 \((x_2,y_2)\)
在向上返回的过程中,对应方程的解也会发生改变
若上一层返回的解为 \((x_2,y_2)\) ,则本层对应的解为 \((y_2,x_2-y_2\lfloor\frac a b \rfloor)\)
那么在递归的过程中使用 exgcd(b,a%b,y,x) 就可以让 x 直接获得解,然后只需要计算 y 的值就好了
在 C++ 中,整数除法默认向下取整,故可以写成 y=y-a/b*x 的形式
根据递归求解的正确性,可以确定递归结果是正确的
\({\huge\color{red}{注意,这里的x与y 的正负是未知的}}\)
两个定理
定理一:对于不定方程 \(ax+by=c,c\%\gcd(a,b)=0\) ,一定存在整数解为 \((\frac c {\gcd(a,b)}x,\frac c {\gcd(a,b)}y)\) (这个应该没必要证明了吧)
定理二:若 \((x,y)\) 是不定方程 \(ax+by=\gcd(a,b)\) 的一组整数解,则 \((x+k\frac b {\gcd(a,b)},y-k\frac a {\gcd(a,b)})\) 也是方程的一组整数解
应用扩展欧几里得求乘法逆元
定义
对于 \(ax\equiv 1 \left(\text{mod}\ p\right)\) ,\(x为a在模p意义下的逆元\)
换言之,对于方程 \(ax+by=1,\gcd(a,b)=1\) , \(x\in\left[1,b\right)\) 是 \(a\) 在模 \(b\) 意义下的逆元
用法
那么我们就可以用 exgcd 求解这个方程,这样就得到了逆元
//扩展欧几里得
template<class T>inline T gcd(T a,T b,T&x,T&y){
if(b==0){
x=1,y=0;return a;
}
T g=gcd(b,a%b,y,x);
y=y-a/b*x;
return g;
}
//求 a 在模 p 意义下的逆元
inline int sol(int a,int p){
int x,y;
int g=gcd(a,p,x,y);
int ans=x,t=0;
//确保 x in [1,p)
while((ans=x+b/g*t)<1)t++;
while((ans=x+b/g*t)>=p)t--;
return ans;
}
这里用到了一个定理:
定理二:若 \((x,y)\) 是不定方程 \(ax+by=\gcd(a,b)\) 的一组整数解,则 \((x+k\frac b {\gcd(a,b)},y-k\frac a {\gcd(a,b)})\) 也是方程的一组整数解
作用是确保 \(x\in\left[1,p\right)\)
e.g.
给定 \(n\) 个正整数 \(a_i\) , \(p=10^9+7\) ,求
\[\sum_{i=1}^{n}{(a_i^{-1}\times 998244353^{n-i})(\text{mod}\ p)} \]
#include<bits/stdc++.h>
using namespace std;
#define GO(u,v,i) for(int i=u;i<=v;i++)
template<class t>inline t fr(){
register t num=0,dis=1;
register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')dis=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
return num*dis;
}
template<class t>inline void fw(t num){
if(num>9)fw(num/10);
putchar(num%10+'0');
}
template<class t>inline void fw(t num,char ch){
if(num<0)num=-num,putchar('-');
fw(num);putchar(ch);
}
const int maxn=5e6+12;
typedef long long lld;
const lld p=1e9+7,k=998244353;
int n;
lld a[maxn],s[maxn],sns[maxn],an[maxn];
lld anss;
template<class t>inline t gcd(t a,t b,t&x,t&y){
if(b==0){
x=1,y=0;
return a;
}
t g=gcd(b,a%b,y,x);
y=y-a/b*x;
return g;
}
template<class t>inline t power(t num,t tim){
t ans=1;
GO(1,tim,i)ans*=num;
return ans;
}
inline void sol(){
n=fr<int>();
s[0]=1;//初始化
GO(1,n,i){
a[i]=fr<lld>();
s[i]=(s[i-1]*a[i])%p;//根据公式(1)
}
lld x,y;
lld g=gcd(s[n],p,x,y);
lld ans=x,t=0;
while((ans=x+p/g*t)<1)t++;
while((ans=x+p/g*t)>=p)t--;
sns[n]=ans;// 1/bn 已求出
lld tmp=1;
for(int i=n;i>0;i--){
sns[i-1]=(sns[i]*a[i])%p;//根据公式(2)
an[i]=(sns[i]*s[i-1])%p;//根据公式(3)
anss+=(an[i]*tmp)%p;//题目要求
tmp*=k;tmp%=p;//tmp就是k^(n-i)
}
fw(anss%p,'\n');
}
signed main(){
sol();
return 0;
}

浙公网安备 33010602011771号