YbtOJ 「数学基础」 第3章 同余问题
同余问题
同余
整除和同余的关系:
当且仅当 当且仅当 ( 为整数)
同余的性质:
-
同加性:
那么 -
同乘性:
那么一般情况下 如果
且 则但同余不满足同除性
-
同幂性:
那么 -
若
且 其中 互质 则
费马小定理
如果
推论:当
欧拉函数
性质:
- 设
其中 为正整数 为质数 那么 - 设
为正整数 且 那么 ( 为 唯一分解的底数(质数))
欧拉定理:
当
扩展欧几里得算法
扩欧证明及解法
同余方程证明及解法
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】同余方程
对于
那么我们
#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】约数之和
对于一个数
易对于
累乘的原因:因为你第一组
等比数列公式:
则
分子可以用快速幂求 分母看是否与
不互质时 需要用到一个结论:如果
我们以二次方为例 设
所以在
所以
此时分母在模意义下为0 因为这个分数为整数 那么分子在模意义下也为0 那么我们用
注意:实现时需要
#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;
}
逆元
当
通俗讲,逆元(
求逆元方法:
-
根据费马小定理
所以可以得知
即为 在 意义下的逆元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 ); }
-
求解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; }
-
线性方法
线性求逆元: 以
复杂度求 的逆元设当前数为
, 设模数 则必有 .将上式转化为
两边同乘
移项得
由于
且 已知 则 可求
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;
}
中国剩余定理
对于很多组
我们设
设
证明:(我们要试着构造出这个解)
对于
所以我两边同乘
所以当
所以当
那么我们将所有这样的
根据同余的同加性 一定可以满足所有方程 再
D. 【例题4】中国剩余定理
模板题 解法见上
观察题解可知这里费马小定理失效(因为模数虽然互质 但是不一定保证是质数) 所以需要用
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探