浅谈数论
逆元
- 逆元是一个非常牛逼的东西。求法很多,又很实用。
- 它可以分为乘法逆元和加法逆元:
-
乘法逆元:如果ab==1(mod p)则我们称b是a关于p的(乘法)逆元;
-
加法逆元:如果a+b=0则我们称b是a的(加法)逆元。
-
a^-1==b(mod p)-> ab==1(mod p)
-
条件:(a,p)=1;
-
整数a对模数p之模逆元存在的充分必要条件是a和p互素,若此模逆元存在,在模数p下的除法可以用和对应模逆元的乘法来达成,此概念和实数除法的概念相同。
模意义下的逆元就好比实数运算中的倒数,模意义下除以一个数就相当于乘上这个数的逆元,就可以实现模意义下的除法啦!
-
线性求逆元
#define ll long long
......
const int p=1e9+7;
int fac[N],inv[N];
ll qpow(ll a,ll b)
{
ll base=a,ret=1;
while(b)
{
if(b&1)ret=(ret*base)%p;
base=base*base%p;
b>>=1;
}
return ret;
}
void gotinv()
{
fac[0]=1;
for(int i=1;i<=N;i++)fac[i]=fac[i-1]*i%p;
inv[N]=qpow(fac[N],p-2)%p;
for(int i=N;i>=1;i--)inv[i-1]=inv[i]*i%p;
}
......
int main()
{
gotinv();
......
}
费马小定理
-
条件:gcd(a,p)=1;
-
a^(p-1)==1(mod p),这是定理精髓,由它我们可以推出好多玄妙的结论。
- 时间复杂度O(log p);
- 这里给出证明:
- 因为{0,1,2,...,(p-1)}为p的完全剩余系,且gcd(a,p)=1,
- 所以a对p的完全剩余系无影响
- 所以{0*a,1*a,...,(p-1)*a}也为p的完全剩余系
- 所以{0*a,1*a,...,(p-1)*a}={0,a,2a,...,(p-1)a}(mod p)等价于{0,1,2,...,(p-1)}(mod p)即a*2a*3a*...*(p-1)a=1*2*3*...*(p-1)(mod p)
- 整理一下:(1*2*...*(p-1))*a^(p-1)=(1*2*3*...*(p-1))(mod p)
- 然后我们会发现这个式子多么优美。
- a^(p-1)==1(mod p)
-
a^(p-1)==1(mod p)可得:
- a^(p-2)==a^-1(mod p)咋一看发现这个式子貌似是乘法逆元,所以说我们多了一种求逆元的方法qaq;
-
这里千万注意gcd(a,p)=1
欧拉函数
- phi(n)我们将其定义为n的完全剩余系中与n互质的数的个数
- 我们现将求法说一下(名字是作者瞎起的)
- 分解质因数法:我们先求出n的质因数p1~pk,则phi(n)=n*(1-1/p1)*(1-1/p2)*...*(1-1/pk)即可,相信正确性是有保证的
- 欧拉筛法:
ll pri[10000002],top,phi[10000002],sum[10000002]; bool pd[10000002]; void prime_shaker() { phi[1]=1; for(ll i=2;i<=n;i++) { if(!pd[i])pri[++top]=i,phi[i]=i-1; for(ll j=1;j<=top&&pri[j]*i<=n;j++) { pd[i*pri[j]]=true; if(i%pri[j]==0) { phi[i*pri[j]]=phi[i]*pri[j]; break; } else phi[i*pri[j]]=phi[i]*phi[pri[j]]; } } for(int i=1;i<=n;i++)sum[i]=sum[i-1]+phi[i]; }
- 暴力QAQ......
欧拉函数性质
-
phi(a*b)=phi(a)*phi(b)
- 任意质数p使得phi(p)=p-1
- 如果gcd(a,n)=1 则 (n-a,n)=1
- n的完全剩余系中所有与n不互质的数的和sum,满足sum=phi(n)*(n/2)
- 如果质数n=p^k(其中p为质数)则phi(n)=p^k-p^(k-1)
- n等于n所有约数的phi的和
欧拉定理
-
if(a,p)=1,a^n=a^(n mod phi(p))modp
因为(a,p)=1
所以x1*x2*...*(xphi(p))==(a*x1)*......*(a*xphi(p))(mod p)
令X=x1*x2*....*xphi(p)
可得X==X*a^phi(p)(mod p)即a^phi(p)==1(mod p)
这里引入欧拉筛法
inline void prime_shaker(int n)
{
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!pd[i])prime[++top]=i,phi[i]=i-1;
for(int j=1;j<top&&1ll*prime[j]*i<=n;j++)
{
pd[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]]=phi[i]*phi[prime[j]]
}
}
}
欧拉是个伟人!
中国剩余定理(Crt)
- crt是用来解同余方程的大法
- x==a1(mod m1)
- x==a2(mod m2)
- ......
- x==ak(mod mk)
- 解如上方程求x。
- 令M=m1*m2*m3*...*mk,Mi=M/mi;
- 所以x=(a1*M1*inv(M1))+...+(ak*Mk*inv(Mk));
扩展中国剩余定理(Excrt)
补:扩展欧几里得exgcd即快速求出ax+by=gcd(a,b)的一组解x,y:
C++:
void exgcd(ll a, ll b, ll& x, ll& y, ll& c)
{
if(!b) {y = 0; x = 1; c = a; return;}
exgcd(b, a % b, y, x); y -= a / b * x;
}
- 假设我们都会了扩展欧几里得,那么我们用它来推一下扩展中国剩余定理。
- 其实扩展crt特别简单...假设你搞出了前k组的一个最小正整数解x,想推出前k+1个方程的解令M=m1*m2*m3*...*mk,Mi=M/mi,显然对于任意t,x+t*M都是满足的.
- 那么对于新的一组方程x==a.k+1(mod m.k+1)我们要找的是令x+t*M==a.k+1(mod m.k+1)最小的t, 即t*M+(m.k+1)*p=a.k+1-x,可以轻松的利用exgcd求出最小解,若某时某刻exgcd无解,则原模线性方程组无解。
代码背下来即可:
typedef long long LL;
LL calc()
{
LL M=m[1],x=a[1];///第一组的解就是a[1],此时M之积为m[1]
for(int i=2;i<=n;i++)
{
LL g=gcd(M,m[i]),t=((a[i]%m[i]-x%m[i])%m[i]+m[i])%m[i];
if(t%g!=0) return -1;//判断exgcd是否有解
LL xx,y;
exgcd(M,m[i],xx,y);
xx=mul(xx,t/g,m[i]/g);//快速乘,防止爆long long
x=xx*M+x;
M=M*(m[i]/g);
x=(x%M+M)%M;
}
return x%M;
}
虽然说mlkit打了这一篇博客,但mlkit那令人发指的数学水平仍然无奈。
那一次又一次的希冀在无穷无尽的失败前跌的粉身碎骨
无穷无尽的期待在绝望面前撞得头破血流qaq