YbtOJ 「数学基础」 第3章 同余问题
同余问题
同余
整除和同余的关系:
- \(a\equiv b\ (mod\ m)\) 当且仅当\(m|(a-b)\)
- \(a\equiv b\ (mod\ m )\) 当且仅当\(a=b+k\times m\) (\(m\)为整数)
同余的性质:
-
同加性:\(a\equiv b\ (mod\ m)\) 那么 \(a+c\equiv b+c\ (mod\ m)\)
-
同乘性:\(a\equiv b\ (mod\ m)\) 那么 \(ac\equiv bc\ (mod\ m)\)
一般情况下 如果\(a\equiv b\ (mod\ m)\) 且\(c\equiv d\ (mod\ m)\) 则\(ac\equiv bd\ (mod\ m)\)
但同余不满足同除性
-
同幂性:\(a\equiv b\ (mod\ m)\) 那么$a^n\equiv b^n\ (mod\ m) $
-
若$a\equiv x\ (mod\ p) $ 且 $a\equiv x\ (mod\ q) $ 其中\(pq\)互质 则$a\equiv x\ (mod\ pq) $
费马小定理
如果\(p\)为质数 那么对于任意一个整数\(a\) 满足\(a^p\equiv a\ (mod\ p)\)
推论:当\(p\)为质数且\(p\)不是\(a\)的约数时 \(a^{p-1}\equiv 1\ (mod\ p)\)
欧拉函数
性质:
- 设\(n=p^k\) 其中\(n,k\)为正整数 \(p\)为质数 那么\(\varphi (n)=p^k-p^{k-1}\)
- 设\(n,m\)为正整数 且\(gcd(n,m)=1\) 那么\(\varphi(n)=\varphi(n) \times \varphi(m)\)
- $ {\textstyle \prod_{i=1}^{n}} (1-\frac 1 {p_i})$ (\(p_i\)为\(n\)唯一分解的底数(质数))
欧拉定理:
当\(gcd(a,n)=1\)时 有\(a^{\varphi(n)} \equiv 1\ (mod\ n)\)
扩展欧几里得算法
扩欧证明及解法

同余方程证明及解法

void exgcd ( int a , int b , int &x , int &y )
{
if ( !b ) { x = 1 , y = 0; return; }
exgcd ( b , a % b , y , x );
y -= ( a / b ) * x;
}
A. 【例题1】同余方程
对于\(ax\equiv 1\ ( mod\ b)\) 保证一定有解 那么\(a,b\)一定互质 所以用扩欧可以求出一组解 但这组解不一定是最小整数解
那么我们\(x\ mod\ b + b\)再\(mod\ b\)即可(因为在模意义下一定有\(gcd(a,b)\)个解)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 1e8 + 1;
const int mod = 1e9 + 7;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
void exgcd ( int a , int b , int &x , int &y )
{
if ( !b ) { x = 1 , y = 0; return; }
exgcd ( b , a % b , y , x );
y -= ( a / b ) * x;
}
int a , b , x , y;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
a = read() , b = read();
exgcd ( a , b , x , y );
cout << ( x % b + b ) % b << endl;
return 0;
}
B. 【例题2】约数之和
对于一个数\(n\),若\(n=p_{1}^{m_{1}} * p_{2}^{m_{2}} \ldots * p_{n}^{m_{n}}\) 。对于\(p_{i}\)在和中贡献 \(\sum_{j=0}^{m_{i}} p_{i}^{j}\) 。则约数和为 \(\prod_{i=1}^{n}\sum_{j=0}^{m_{i}} p_{i}^{j}\) 。
易对于 \(A^{B}\) 来说 \(A n s=\prod_{i=1}^{n}\sum_{j=0}^{B * m_{i}} p_{i}^{j}\)
累乘的原因:因为你第一组\(p_1^{random}\) 和第二组\(p_2^{random}\)中的每一个数都需都可以组成一个因数 那么使用乘法分配律就可以不重不漏地(因为是质数)求得最终的数量和
等比数列公式:
\((1)\): \(x=p^{0}+p^{1} \ldots p^{m}\)
\((2)\): \(p x=p^{1}+p^{2} \ldots p^{m+1}\)
\((2) - (1)\) 则得 $x=\frac{p_{i}^{B\ *\ m_{\ i}\ +\ 1}-1}{p_{i}-1} $ 。
则\(A n s=\prod_{i=1}^{n}\left(\frac{p_{i}^{B\ *\ m_{\ i}\ +\ 1}-1}{p_{i}-1}\right)\) 。
分子可以用快速幂求 分母看是否与\(9901\)互质 如果不互质的话就是\(p_i-1\)的逆元 如果互质 那么\(p_i\ mod\ 9901=1\)
不互质时 需要用到一个结论:如果\(p_i\ mod\ x=b\) 那么\(p^{t}\ mod\ x=b^t\)
我们以二次方为例 设\(p=kx+b\) 那么\(p^2=k^2x^2+2kbx+b^2\)
所以在\(mod\ x\)意义下 \(p^{2}\ mod\ x=b^2\)
所以$p_i^{random} mod\ 9901=1 $
此时分母在模意义下为0 因为这个分数为整数 那么分子在模意义下也为0 那么我们用\(\sum_{j=0}^{B * m_{i}} p_{i}^{j}=B*m_i+1\)来计入答案
注意:实现时需要\(+mod\%mod\)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 5e7 + 5;
const int mod = 9901;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int a , b , n , m , c[N] , prime[N] , cnt , ans = 1;
int ksm ( int x , int k )
{
int base = x , res = 1;
while (k)
{
if ( k & 1 ) res = res * base % mod;
base = base * base % mod;
k >>= 1;
}
return res;
}
void solve ( int x )
{
for ( int i = 2 ; i * i <= x ; i ++ )
if ( x % i == 0 )
{
prime[++cnt] = i;
while ( x % i == 0 )
{
c[cnt] ++;
x /= i;
}
}
if ( x > 1 )
{
prime[++cnt] = x;
c[cnt] ++;
}
}
int inv ( int x ) { return ksm ( x , mod - 2 ); }
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
a = read() , b = read();
solve ( a );
for ( int i = 1 ; i <= cnt ; i ++ )
{
if ( ( prime[i] - 1 ) % mod == 0 ) { ans = ans * ( b * c[i] + 1 ) % mod; continue; }
int mu = inv ( prime[i] - 1 );
int zi = ( ksm ( prime[i] , b * c[i] + 1 ) + mod - 1 ) % mod;
ans = ans * zi * mu % mod;
}
cout << ( ans + mod ) % mod << endl;
return 0;
}
逆元
当\(ax≡1(mod\ b)\), \(x\)的最小整数解 即为\(a\)在\(mod\ b\) 意义下的逆元。
通俗讲,逆元(\(x^{-1}\))j即为\(a\)在\(mod\ b\) 下的倒数
求逆元方法:
-
根据费马小定理 \(a\times a^{p-2}\equiv 1\ (mod\ p)\)
所以可以得知\(a^{p-2}\)即为\(a\)在\(mod\ p\)意义下的逆元
int ksm ( int x , int k ) { int base = x , res = 1; while (k) { if ( k & 1 ) res = res * base % mod; base = base * base % mod; k >>= 1; } return res; } int inv ( int x ) { return ksm ( x , mod - 2 ); } -
\(exgcd\)求解
void exgcd ( int aa , int bb , int &x , int &y ) { if ( !bb ) return x = 1 , y = 0 , void(); exgcd ( bb , aa % bb , y , x ); y -= aa / bb * x; } int inv ( int numm , int modd ) { int x , y; exgcd ( numm , modd , x , y ); return ( x % modd + modd ) % modd; } -
线性方法
线性求逆元: 以\(O(n)\)复杂度求\(1-n\)的逆元
设当前数为\(x\), 设模数\(m=\left\lfloor\frac{m}{x}\right\rfloor \cdot x+r\) 则必有\(x>r\) .
将上式转化为\(m=\left\lfloor\frac{m}{x}\right\rfloor \cdot x+r\equiv 0(\bmod m)\)
两边同乘 \(x^{-1} \cdot r^{-1}:\left\lfloor\frac{m}{x}\right\rfloor \cdot r^{-1}+x^{-1} \equiv 0(\bmod m)\)
移项得$ x^{-1}=\left\lfloor\frac{m}{x}\right\rfloor \cdot\left(-r^{-1}\right) \bmod m$
由于\(x>r\)且 \(r^{-1}\)已知 则\(x^{-1}\)可求
inv[1] = 1;
for ( int i = 2 ; i <= n ; i ++ ) inv[i] = ( ( m - m / i ) * inv[m%i] + m ) % m;
C. 【例题3】线性求逆元
模板题
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 3e6 + 5;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int a[N] , n , m , inv[N];
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
inv[1] = 1;
for ( int i = 2 ; i <= n ; i ++ ) inv[i] = ( ( m - m / i ) * inv[m%i] + m ) % m;
for ( int i = 1 ; i <= n ; i ++ ) cout << inv[i] << endl;
return 0;
}
中国剩余定理
对于很多组\(x\equiv a_i\ (mod\ m_i)\) 其中\(m_i\)为两两互质的质数 我们的目标是找出\(x\)的最小整数解
我们设\(M\)为\(m_1\)到\(m_n\)的最小公倍数 那么\(M=\prod_{i=1}^n m_i\)
设\(k_i=M/m_i\) 并找到\(k_i\)的逆元\(b_i\) 那么最小解即为\(\prod_{i=1}^nm_ik_ib_i\ (mod\ M)\)
证明:(我们要试着构造出这个解)
对于\(x\equiv a_i\ (mod\ m_i)\) 我们设\(x=a_i\) 显然成立 但是这个解只满足这一个方程 但不一定满足其他的同余方程
所以我两边同乘\(\frac {M}{m_i}\) 即\(a_i\frac {M}{m_i}\equiv a_i\frac {M}{m_i}(mod\ m_i)\)(同余的同乘性)
所以当\(x=a_i\frac {M}{m_i}\)时 除了第\(i\)个方程 所有的\(1-n\)个方程都满足\(x\equiv 0(mod\ m_i)\) 但是我们为了好算 两面同乘\((\frac{M}{m_i})^{-1}\)
所以当\(x=a_i\frac {M}{m_i}(\frac{M}{m_i})^{-1}\)时 对于第\(i\)个方程 \(x\equiv a_i(mod\ b)\) 除了第\(i\)个方程的所有方程都满足\(x\equiv 0(mod\ b)\)
那么我们将所有这样的\(a_i\frac {M}{m_i}(\frac{M}{m_i})^{-1}\)加起来即可 最终答案就是\(\sum_{i=1}^na_i\frac {M}{m_i}(\frac{M}{m_i})^{-1} mod\ M\)
根据同余的同加性 一定可以满足所有方程 再\(mod\ M\)得到一个最小解
\(p.s.\) \(mod\ M\)的原因:对于每一个方程 你成倍地加上或减去\(M\) 对同余性是没有影响的(因为\(M\)一定是模数的倍数 所以消掉了)
D. 【例题4】中国剩余定理
模板题 解法见上
观察题解可知这里费马小定理失效(因为模数虽然互质 但是不一定保证是质数) 所以需要用\(exgcd\)求解逆元
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , a[N] , b[N] , ans = 0 , M = 1 , k[N] , r[N];
//int ksm ( int x , int k , int mod )
//{
// int ans = 1 , base = x;
// while (k)
// {
// if ( k & 1 ) ans = ans * base % mod;
// base = base * base % mod;
// k >>= 1;
// }
// return ans;
//}
//
//int inv ( int x , int mm ) { return ksm ( x , mm - 2 , mm ); }
void exgcd ( int aa , int bb , int &x , int &y )
{
if ( !bb ) return x = 1 , y = 0 , void();
exgcd ( bb , aa % bb , y , x );
y -= aa / bb * x;
}
int inv ( int numm , int modd )
{
int x , y;
exgcd ( numm , modd , x , y );
return ( x % modd + modd ) % modd;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , b[i] = read() , M *= a[i];//模数 余数
for ( int i = 1 ; i <= n ; i ++ ) k[i] = M / a[i];
for ( int i = 1 ; i <= n ; i ++ )
ans = ( ( ans + b[i] * k[i] * inv ( k[i] , a[i] ) ) % M + M ) % M;
cout << ans % M << endl;
return 0;
}

浙公网安备 33010602011771号