中国剩余定理
解同余方程组 \(\begin{cases}x\equiv a_1\pmod{m_1}\\ x\equiv a_2\pmod{m_2}\\ \qquad\cdots\\ x\equiv a_n\pmod{m_n}\end{cases}\)
其中 \(m_i\) 两两互质。
令 \(M=\prod\limits_{i=1}^nm_i\),\(M_i=\prod\limits_{j\ne i}m_j=\dfrac M{m_i}\),
设 \(M_i^{-1}\) 表示 \(M_i\bmod m_i\) 的逆元(注意模数),则方程组的解为
正确性不难验证
模板题:洛谷P1495 【模板】中国剩余定理(CRT)/曹冲养猪
扩展中国剩余定理
方程组同上,但不保证 \(m_i\) 两两互质。
核心思想在于合并两个同余方程的操作
以 \(\begin{cases}x\equiv a_1\pmod{m_1}\\ x\equiv a_2\pmod{m_2}\end{cases}\) 为例
设 \(x=m_1k_1+a_1=m_2k_2+a_2\),则 \(m_1k_1-m_2k_2=a_2-a_1\)。
经典的二元一次不定方程。无解条件是 \(\gcd(m_1,m_2)\nmid a_2-a_1\)。
如果有解,可用 exgcd 得到一组可行解 \(k_1,k_2\)。
列出新方程:\(x\equiv m_1k_1+a_1\pmod{{\rm lcm}(m_1,m_2)}\)。
这样就将两个方程合并成了一个。\(n-1\) 次合并之后即可求出原方程组的解。
模板题:洛谷P4777 【模板】扩展中国剩余定理(EXCRT)
BSGS
求方程 \(a^x\equiv b\pmod p\) 的最小非负整数解。其中 \(a,p\) 互质。
考虑欧拉定理:\(a,p\) 互质时,\(a^{\varphi(p)}\equiv 1\pmod p\)。
易知最小非负整数解一定在 \([0,p)\) 内,如果这个区间内找不到解那就是无解。
设 \(x=im-j(1\le j\le m)\),其中 \(m=\lceil\sqrt p\rceil\),则
枚举 \(j=1\sim m\) 并计算 \(b\cdot a^j\) 的值,存入 hash 表;
枚举 \(i=1\sim m\),计算 \((a^m)^i\) 的值,查表寻找解。
枚举顺序为从小到大,并且当 \(b\cdot a^j\) 的值出现重复时,用较大的 \(j\) 覆盖较小的。
这样就保证了最先找到的一定是最小非负整数解。没找到则无解。
时间复杂度 \(O(\sqrt p)\),如果 hash 表是用 map
实现的就带个 log
模板题:洛谷P3846 [TJOI2007] 可爱的质数/【模板】BSGS
exBSGS
求方程 \(a^x\equiv b\pmod p\) 的最小非负整数解。不保证 \(a,p\) 互质。
设 \(g=\gcd(a,p)\)
\(g\nmid b\) 时是无解的。
由于 \(\dfrac ag\) 与 \(\dfrac pg\) 互质,逆元一定存在。
此时得到的方程与原方程形式相同,但模数变为了 \(\dfrac pg\)
容易想到递归处理,直到 \(a\) 与模数互质,即可用 BSGS 解决。
\(p\) 变为 \(\dfrac pg\) 至少减半,因此递归层数 \(k\) 不超过 \(\log p\)。
实际上还要考虑 \(x<k\) 的情况,所以先枚举 \(x=0\sim k\),判断是否存在解,有解则直接返回,否则再执行上述过程。
时间复杂度一般为 \(O(\sqrt p)\),瓶颈在底层的 BSGS 。
模板题:洛谷P4195 【模板】扩展 BSGS/exBSGS
代码还是挺细节的,,贴一下吧(虽然丑得没法看
实际上式子化为 \(\displaystyle{a^{x-k}\cdot\prod\frac ag\equiv \frac b{\prod g}\pmod{\frac p{\prod g}}}\) 就可以用 BSGS 做了,没必要求逆元
#include<stdio.h>
#include<math.h>
const int P=10007;
int a,b,p,x,y,g,m,k,t,t2,s,cnt,lst[P];
struct node { int x,j,pre; }mp[40000]; // 手写 hash 表
inline void ins(int x,int j) { // insert
mp[++cnt]={x,j,lst[x%P]},lst[x%P]=cnt;
}
inline int fnd(int x) { // find
for (int i=lst[x%P]; i; i=mp[i].pre)
if (mp[i].x==x) return mp[i].j;
return 0;
}
int gcd(int x,int y) { return y?gcd(y,x%y):x; }
void exBSGS(int a,int b,int p) {
if (b==1) { puts("0"); return ; } // 需要特判
for (int i=1; i<=cnt; ++i) lst[mp[i].x%P]=0;
s=t=1,k=0,cnt=0;
while ((g=gcd(a,p))!=1) {
if (b%g) { puts("No Solution"); return ; }
t=1ll*t*(a/g)%(p/=g),b/=g,++k;
if (t==b) { printf("%d\n",k); return ; }
}
m=ceil(sqrt(p));
for (int i=1; i<=m; ++i)
s=1ll*s*a%p,ins(1ll*s*b%p,i);
for (int i=1; i<=m; ++i) // 循环开始时 t 的值即为 Π(a/g)
if (t2=fnd(t=1ll*t*s%p)) {
printf("%d\n",i*m-t2+k);
return ;
}
puts("No Solution");
}
int main() {
scanf("%d%d%d",&a,&p,&b);
while (p)
exBSGS(a%p,b%p,p),
scanf("%d%d%d",&a,&p,&b);
return 0;
}