同余 与 数论其他
一.定理
费马小定理&欧拉定理
-
若 \(p\) 为质数且 \(a \not \equiv 0\pmod p\),则 \(a^{p-1}\equiv 1\pmod p\).
-
若 \(\gcd(a,m)=1\),则 \(a^{\varphi(m)}\equiv 1\pmod m\).
-
(扩展欧拉定理)若 \(b\ge\varphi(p)\),则有 \(a^{b}\equiv a^{b\bmod \varphi(p)+\varphi(p) }\pmod p\).
二.算法
欧几里得算法
给定 \(a,b\),求 \(\gcd(a,b)\)。
我们发现,有:
利用这个东西递归计算即可。
扩展欧几里得(exgcd)
- 记 \(g=\gcd(a,b)\),求解下列方程:\(ax+by=g\)
设计函数 exgcd(ll a,ll b,ll &x,ll &y)。
当 \(b=0\) 时,我们解此时的方程 \(a_0x+b_0y=g=a_0\),只需令 \(x=1,y=0\) 即可。
若已经知道了 \(bx+(a\bmod b)y=g\) 的一组解 \(x_0,y_0\),如何借出方程 \(ax+by=g\) 的一组解?
于是我们就得到了一组解 \(x=y_0,y=x_0-\lfloor \frac{a}{b} \rfloor \cdot y_0\),不停地向上回溯即可解出原方程的解。
代码:
ll exgcd(ll a,ll b,ll &x,ll &y){
if(!b){ x=1,y=0;return a; }
ll d=exgcd(b,a%b,x,y);
ll z=x; x=y; y=z-y*(a/b);//妈妈生的!!a/b要带括号!!!
return d;
}
注:
方程 \(ax+by=c\) 当且仅当 \(\gcd(a,b)\,\mid \,c\) 时有解。
线性同余方程
求解同余方程 \(ax\equiv b\pmod m\)。
只需解出 \(ax-my=b\) 的一组解即可,运用 exgcd 即可。
当且仅当 \(\gcd(a,m)\,\mid \,b\) 时有解。
//解同余方程 ax≡b(mod m)
ll TYFC(ll a,ll b,ll m){
ll x,y,mm;
ll G=exgcd(a,m,x,y);
if(b%G) return -1;
x*=b/G;mm=m/G;
return (x%mm+mm)%mm;
}
类欧几里得算法,万能欧几里得算法与 SB 树
单独写一篇博客了,在这里。
乘法逆元
同余方程 \(ax\equiv 1\pmod m\) 的解 \(x = a^{-1}\) 记作 \(a\) 模 \(m\) 的乘法逆元。
\(m\) 为奇质数
此时由费马小定理 \(a^{m}\equiv a\pmod m\),两边同时除以 \(a^2\) 得 \(a^{m-2}\equiv a^{-1}\pmod m\),于是直接快速幂就好。
如果要 \(O(n)\) 求出 \(1\sim n\) 得所有数逆元,可以处理处阶乘和阶乘逆元。
\(m\) 不为质数
要求 \(a\) 和 \(m\) 互质时 \(a^{-1}\) 才存在。
可以试做方程 \(ax+my\equiv 1\pmod m\) 这个方程 \(x\) 的一个解。
于是使用 exgcd 解方程就行。
中国剩余定理
参考:蓝书、oi-wiki。
用来求解以下方程组:
其中满足 \(\forall m_i\),两两互质。
我们记 \(m = \prod\limits_{i=1}^{n}m_i\),\(M_i = \dfrac{m}{m_i}\);记 \(t_i\) 为 \(M_i\) 模 \(m_i\) 下的逆元,即为 \(M_it_i\equiv 1 \pmod {m_i}\) 的一个解。
于是我们有 \(x\) 的整数解:
证明也很简单,考虑方程 \(x_0\equiv a_k \pmod {m_k}\),对于所有的 \(i\not = k\),显然 \(M_i = \dfrac{m}{m_i}\) 为 \(m_k\) 的倍数,故 \(a_iM_it_i\equiv 0 \pmod {m_i}\)。
而对于 \(a_kM_kt_k\) 项来说,因为 \(M_kt_k \equiv 1 \pmod {m_k}\),故 \(a_kM_kt_k \equiv a_k \pmod {m_k}\),方程组成立。
此时 \(x\) 的通解为 \(x = km+x_0\)。
扩展中国剩余定理
上述情况下如果不保证 \(m_i\) 两两互质。
蓝书是这么做的:
假设求出了 \(x_{k-1}\) 满足前 \(k-1\) 个方程,记 \(M_{k-1} = \overset{k-1}{\underset{i=1}{\operatorname{lcm}}} \;m_i\),则前 \(k-1\) 个方程的通解为 \(x = x_{k-1}+tM_{k-1}\)。
考虑第 \(k\) 个方程,相当于 \(x_{k-1}+tM_{k-1}\equiv a_k \pmod {m_k}\)。方程等价于 \(M_{k-1}t\equiv a_k-x_{k-1}\pmod {m_k}\),其中只有 \(t\) 是变量。
解这个方程得到 \(t\),于是有了前 \(k\) 个方程的解 \(x_k=x_{k-1}+tM_{k-1}\)。
如此来 \(n\) 遍就求出了所有方程的解。其中任何一步如果不满足 \(\gcd(M_{k-1}, m_k)\mid (a_k-x_{k-1})\) 则无解。
阶与原根
参照这篇博客
一.定义
由欧拉定理,对于整数 \(a\) 和 正整数 \(m\),若有 \(\gcd(a,m)=1\),则:
也就是说,满足 \(a^n\equiv 1\pmod m\) 的最小正整数 \(n\) 一定存在,这个 \(n\) 称作 \(a\) 模 \(m\) 的阶,记作 \(\delta_m(a)\)。
如果有 \(\delta_m(a)=\varphi(m)\),则称 \(a\) 为模 \(m\) 的原根。
二.性质
证明咕了。
当且仅当 \(m=1,2,4,p^n,2p^n\) 时,\(m\) 有原根。
证明咕了。
若 \(m\) 有原根,则 \(m\) 共有 \(\varphi(\varphi(m))\) 个原根。
证明咕了。
若 \(m\ge 3,\gcd(g,m)=1\),则当且仅当对于 \(\varphi(m)\) 的每一个素因子 \(p\),有:
时,\(g\) 为模 \(m\) 的原根。
离线对数问题
求解方程 \(a^x\equiv b\pmod p\)。
BSGS算法
此部分来源于算法竞赛进阶指南。
全称为 BabyStepGiantStep,适用于 \(\gcd(a,p)=1\)。
为何必须限定 \(\gcd(a,p)=1\)?因为只有在满足 \(a,p\) 互质时,模 \(p\) 意义同余方程两边才能同时进行关于 \(a\) 的加法、乘法运算。
由欧拉定理,\(a^x\) 每 \(\varphi(p)\) 次就会出现循环节,故解一定小于 \(p\)。
设解为 \(x=i\cdot t-j\),其中 \(t=\lceil\sqrt{p} \rceil,0\le j < t\),则有:
即:
我们枚举 \(j\in[0,t-1]\),将 \(b\cdot a^j\bmod p\) 插入一个 map 中。
然后我们枚举 \(i\in[0,t]\) ,计算 \(a^{i\cdot t}\bmod p\),在 map 中查找是否存在对应的 \(j\),更新答案即可。
时间复杂度 \(O(\sqrt p)\)。
点击查看代码
ll BSGS(ll a,ll b,ll p){
if(a%p==0){
if(b%p==1&&a!=0) return 0;
else if(b%p==0) return 1;
else return -1;
}
map <ll,int> mp; mp.clear();
b%=p;ll t=ceil(sqrt((double)p)),now=b;
for(int j=0;j<t;j++){
if(j) (now*=a)%=mod;
mp[now]=j;
}
now=1;a=qpow(a,t,p);
for(int i=0;i<=t;i++){
if(i) (now*=a)%=mod;
if(mp.find(now)!=mp.end()&&i*t-mp[now] >=0) return i*t-mp[now];
}return -1;
}
扩展BSGS算法
还是求解这个方程,但这里没有 \(\gcd(a,p)=1\)。
我们做如下处理:
同余式不好处理,我们令:
记 \(t=\gcd(a,p)\)
若 \(t\nmid b\),无解,除非 \(b=1\) 时解为 \(x=0\)。
若 \(t=1\),直接 BSGS 就行了。
其他情况下,我们让式子左右同时除以 \(t\):
即:
由于此时 \(\dfrac{a}{t}\) 与 \(\dfrac{p}{t}\) 互质,故 \(\dfrac{a}{t}\) 在模 \(\dfrac{p}{t}\) 意义下有逆元。由于 \(\dfrac{p}{t}\) 不是质数,费马小定理不适用,我们用 exgcd 求出逆元,于是有:
发现这个问题和原问题一模一样,于是递归求解。
递归层数不超过 \(\log n\) 级别。
点击查看代码
ll TYFC(ll a,ll m){
ll x,y; exgcd(a,m,x,y);
return (x%m+m)%m;
}
ll exBSGS(ll a,ll b,ll p){
b%=p; if(b==1||p==1) return 0;
ll t=gcd(a,p);
if(b%t) return -INF;
if(t==1){
return BSGS(a,b,p);
}
ll inv=TYFC(a/t,p/t);
return exBSGS(a,b/t*inv,p/t)+1;
}
高次剩余
二次剩余
其他
斐波那契相关
\(O(1) \gcd\)
不是,这玩意太邪门了。
我们记值域为 \(m\),\(t=\sqrt m\),于是可以 \(O(m)\) 预处理,\(O(1)\) 求 \(\gcd\)。
参考了这三篇博客:OneInDark,weixin_30414635
,hhoppitree
证明去翻这几篇博客吧,我反正是通篇不写证明,有空再写。
一.引理
对于任意正整数 \(x\le m\),均能够把 \(x\) 分为 \(x=abc\),其中 \(a,b,c\) 这三个数,要么 \(<\sqrt m\),要么是质数。
二.算法流程
我们预处理这些东西:
- \(\sqrt m\) 以内的 \(\gcd\) 表,复杂度为 \(O(\sqrt m \cdot \sqrt m)=O(m)\)
代码:
for(int i=1;i<=t;i++){
gcd[i][0]=gcd[0][i]=i;
for(int j=1;j<=t;j++){
gcd[i][j]=gcd[j%i][i];
}
}
- \(m\) 以内正整数的分解。
对于正整数 \(x\),假设它的最小质因数为 \(v\),\(\dfrac{x}{v}\) 分解为 \(abc\),则我们对其中最小的一个乘以 \(v\),即可得到 \(x\) 的分解。
代码:
split[1][0]=split[1][1]=split[1][2]=1;
for(int i=2;i<=maxm-10;i++){
ll minn=INF,mini;
for(int j=0;j<3;j++){
split[i][j]=split[i/v[i]][j];
if(split[i][j] < minn) minn=split[i][j],mini=j;
}
split[i][mini] *= v[i];
}
接下来我们尝试实现 \(\gcd(x,y)\)。
首先,对于 \(x,y\le \sqrt m\),直接返回算好的 \(\gcd\) 表。
我们将 \(x\) 分解为 \(abc\)(下述中记为 \(x_0x_1x_2\)),然后分别用每个数和 \(y\) 计算 \(\gcd\),再合并答案。
具体的,每局 \(i\in[0,3)\),对于 \(x\) 的一个分解 \(x_i\),若 \(x_i\le \sqrt m\),则我们直接计算 \(d=\gcd(x_i,y\bmod x_i)\);否则,\(x_i\) 一定是质数,则如果 \(y\bmod x_i=0\),取 \(d=x_i\),否则取 \(d=1\)。
然后我们让 \(ans\leftarrow ans*d\),\(y\leftarrow\dfrac{y}{d}\),即可。
代码:
ll Gcd(ll x,ll y){
if(x<=1010 && y<=1010) return ggcd[x][y];
ll res=1,tmp;
for(ll i=0;i<3;i++){
if(split[x][i]==1) continue;
if(split[x][i] <= 1010) tmp=ggcd[split[x][i]][y%split[x][i]];
else if(y%split[x][i]==0) tmp=split[x][i];
else tmp=1;
res *= tmp;y/=tmp;
}return res;
}

浙公网安备 33010602011771号