余数定理学习指南
前置芝士
乘法逆元
a存在模p的乘法逆元的充要条件是gcd(a,p)=1,即a与p互质。
若a,b不互素,则a必然不存在模b的逆元。
0没有逆元
余数性质
如果a,b除以m的余数相同,那么a与b的差是m的倍数。(作差是为了消掉余数)
剩余类
给定一个正整数 n,把所有整数根据模 n的余数 r∈[0,n−1]分为 n 类,每一类数都是诸如 Cr=n∗x+r, 的形式,这样的一类数所构成的一个集合称为模 n的剩余类。
剩余系
给定一个正整数 n,有 n个不同的模 n的剩余类,从中任选 x个不同的剩余类,从这 x个剩余类中各取出一个元素,总共 x 个数,将这些数构成一个新的集合,则称这个集合为模 n的剩余系。
完全剩余系
给定一个正整数 n,有 n个不同的模 n的剩余类,从这 n个不同的剩余类中各取出一个元素,总共 n个数,将这些数构成一个新的集合,则称这个集合为模 n的完全剩余系。、
我们取 n=5,则 {0,1,2,3,4}是一个模 5的完全剩余系,{5,1,8,−3,14}也是一个模5 的完全剩余系。
对于一个模 n的完全剩余系 r,若有 a∈Z, b∈Z,且 gcd(n,a)=1,则 也构成一个模 n 的完全剩余系。
简化剩余系
给定一个正整数 n,有 φ(n)个不同的、模 n的余数 r 与 n互质的剩余类,从这 φ(n)个剩余类中各取出一个元素,总共 φ(n)个数,将这些数构成一个新的集合,则称这个集合为模 n的简化剩余系。
我们取 n=10,则 {1,3,7,9} 是一个模 10的简化剩余系。
[性质1]
对于一个模 n 的简化剩余系 r,若有 a∈Z且 gcd(n,a)=1,则 也构成一个模 n 的简化剩余系。
[证明]
因为(a,m)=1,(x,m)=1,于是(ax,m)=1。故ax为m的简化剩余类的剩余。
因为φ(n)是固定不变的,只要通过反证法证明这个数模m两两不同余即可。若存在和,i!=j,使得。
若上面等式成立,需要满足,由于gcd(a,m)=1,所以,这与模m两两不同余矛盾。因此,ax也是模m的一个简化剩余系。
互质对
如果整数x和y的最大公因数是1,则称(x,y)为互质对。
有理数
。。。。。。
分数
[判断分数是有限小数]
一个最简分数,分母的质因数中只有2或5,这个分数一定能化成有限小数
扩展欧几里得
x的第一个正解就是(x%k+k)%k,其中,k=b/gcd(a,b)
[值域分析]
ax+by=gcd(a,b)的解法有无数个,显然其中有的解会爆long long
但万幸的是,若,扩展欧几里得算法求出的可行解必有。
// 求x, y,使得ax + by = gcd(a, b)
int exgcd(int a, int b, int &x, int &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a/b) * x;
return d;
}
欧拉函数
欧拉函数φ(n) 是小于或等于n的正整数中与n互质的数的数目。
公式:
[性质]
(1)积性函数,但不完全积性函数
当n,m互质时,满足
当n根据算术基本定理分解为时,
【证明】
若n与m互质,则n与m没有相同的质因子设n的质因子个数为,m的质因子个数为,则
(2)对于质数p,它的欧拉函数值ϕ(p)=p−1
[证明]
因为p为质数,所以比它小的数都和它互质,即在1~p中共有p-1个数和它互质。
(3)当n为奇数时,ϕ(2∗n)=ϕ(n)
[证明]
当n为奇数时,n与2互质,由欧拉函数是积性函数可知,n与2互质时,
又因为ϕ(2)=1,所以ϕ(2n)=ϕ(n).
(4)当,
(5)n中与n互质的数的和为,并且ϕ(n)(n>2)为偶数。
(6),
(7)
(8)
欧拉定理
[定义]
费马小定理,其中gcd(a,p)=1,是欧拉定理的一种特性。
[证明]
。。。。。。
扩展欧拉定理
[公式]
[证明]
。。。。。。
快速幂
递归方式:log(n)
【思路分析】
求: ①若n为偶数,; ②若n为奇数,。
//c++ 递归
int qmi(int x,int n){
if(n==0) return 1;
int res=qmi(x,n/2);
return n%2==0?y*y:y*y*x;
}
迭代方式:log(n)
【思路分析】
求: 11的二进制:1011,于是, 只要分别求x^i再相乘,不用将x连乘11次。
//c++ lambda while
auto qmi=[](int a,int k,int p){
ll res=1%p,t=a;
while(k){
if(k&1) res=res*t%p;
t=t*t%p;
k>>=1;
}
return res;
};
快速幂(小数、不含取模)
-100.0<x<100.0
double qmi(double x,ll n){
ll en=n;
if(n<0) en=-n;
double res=1;
while(en){
if(en&1) res*=x;
x*=x;
en>>=1;
}
if(n>0) return res;
return 1.0/res;
}
BSGS算法
给定一个质数 ,以及一个整数 ,一个整数 ,现在要求你计算一个最小的非负整数 ,满足 。
Baby Step Giant Step
(1)由扩展欧拉定理,可知模p意义下的最小循环节为,因,故考虑,必能找到最小整数x。
(2)暴力枚举的时间复杂度为O(p)的。
(3)令,其中
则,转化为。
(4)先枚举j,把(ba^j,j)丢进哈希表,如果key重复,用更大的j代替旧值。
(5)再枚举i,计算,在哈希表中查找是否又相等的key,找到第一个即结束,则最小的x=im-j。
扩展BSGS算法
给定 ,求满足 的最小自然数 。
[解题步骤]
(1)当a,p互质时,可以使用BSGS算法求解。
(2)当a,p不互质时,想办法让他们变得互质。
(3)原方程等价于
设,则原方程无解。
否则方程两边同时除于,得
如果a和仍不互质再除
设,,则原方程无解。
或者方程两边需要同时除于,得
不停的判断下去,直到,记
原方程变为
因,则,则就有逆元了,把它丢到方程右边,就是一个标准的BSGS问题了,求解出x-k后再加上k就ok了。
求解正整数n的欧拉函数
O(sqrt(n))
//求n的欧拉函数值
int phi(int n){
int res=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
求解[1,n]区间的欧拉函数值(线性筛)
O(nlognlogn)
int phi[N];
void init(int n){
for(int i=1;i<=n;i++) phi[i]=i;
for(int i=2;i<=n;i++)
if(phi[i]==i)
for(int j=i;j<=n;j+=i){
phi[j]=phi[j]/i*(i-1);
}
}
O(n)
const int N = 10000010;
int phi[N];
ll sum[N];
bool vis[N];
int pr[1000010], idx;
void init(int n) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i])
pr[idx++] = i, phi[i] = i - 1;
for (int j = 0; j < idx && pr[j]*i <= n; j++) {
vis[i*pr[j]]=1;
if(i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j];
break;
}else{
phi[i*pr[j]]=phi[i]*phi[pr[j]];
}
}
}
}
求解[l,r]范围区间的欧拉函数值
计算小于等于x的数中,与x不互质的数的个数。
()
O(r-l)
const int N = 1e6 + 10;
const ll mod = 666623333;
int pr[N], cnt;//保存的是每一个质数,以及个数cnt
bool vis[N];
void get_pr(int n) {
for (int i = 2; i<= n; i++) {
if (!vis[i]) pr[cnt++] = i;
for (int j = 0; pr[j] <= n / i; j++) {
vis[pr[j]*i] = true;
if (i % pr[j] == 0) break;
}
}
}
// N=1e6 + 10;
vector<int> ans[N];
ll phi[N];
void solve() {
ll l, r, res=0;
cin>>l>>r;
get_pr((int)sqrt(r));//预处理出<=sqrt(r)的质数
for(int i=0;i<cnt;i++){
int p=pr[i];
for(ll j=((l-1)/p+1)*p;j<=r;j+=p)
ans[j-l].push_back(p);
}
for(ll i=l;i<=r;i++){
ll tmp=i;
phi[i-l]=i;
for(int p:ans[i-l]){
phi[i-l]=phi[i-l]/p*(p-1);
while(tmp%p==0) tmp/=p;
}
if(tmp>1) phi[i-l]=phi[i-l]/tmp*(tmp-1);
//phi:互质的数
//i-phi:不互质的数
res=(res+i-phi[i-l])%mod;
}
cout<<res<<endl;
}
高精模幂数降幂
费马小定理降幂
推论 若p为素数, 则对一切a,都有a^p===a(mod p)。注意这里是一切a,即a和p不一定互素。
若p为素数,且p不能整除a(或能整除则结果为0),有
欧拉降幂
[problem description]
求
[solved]
ll a,b,m,phi;
bool flag;
string s;
int get_phi(int n){//求欧拉函数
int res = n;
for(int i=2; i*i<=n; i++){
if(n%i == 0){
res = res/i*(i-1);
while(n%i == 0) n /= i;
}
}
if(n>1) res = res/n*(n-1);
return res;
}
int depow(ll phi){//降幂
ll b = 0;
int len=s.size();
for(int i=0;i<len; i++){
b = b*10+(s[i]-'0');
if(b>=phi) flag=1, b%=phi;
}
if(flag) b += phi;
return b;
}
int qmi(ll a, ll b){//快速幂
int res = 1;
while(b){
if(b&1) res = res*a%m;
a = a*a%m;
b >>= 1;
}
return res;
}
void solve(){
cin>>a>>m>>s;
phi = get_phi(m);
b = depow(phi);
cout<<qmi(a,b)<<endl;
}
同余方程[]
不定方程[]
[solved]
扩展欧几里得算法实现
当b=0时,ax+by=a=>x=1,y=0
当b!=0时,gcd(a,b)=gcd(b,a%b)
[公式推导]
所以
可以通过递归算法,先求出下一层的,,再回代到上一层,层层回代,可求特解。
通解:
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0) {x=1, y=0; return a;}
ll d = exgcd(b, a%b, y, x);
y-=a/b*x;
return d;
}
void solve(){
ll a,b,c;
cin>>a>>b>>c;
ll x,y;
ll d=exgcd(a,b,x,y);
if(c%d==0) cout<<x<<" "<<y>>endl;//d|c才有整数解
else cout<<"No"<<endl;
}
不定方程[]
[solved]
有整数解的充要条件:gcd(a,b)|c
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0) {x=1, y=0; return a;}
ll d = exgcd(b, a%b, y, x);
y-=a/b*x;
return d;
}
void solve(){
ll a,b,c;
cin>>a>>b>>c;
ll x,y;
ll d=exgcd(a,b,x,y);
if(c%d==0) cout<<c/d*x<<" "<<c/d*y>>endl;//d|c才有整数解
else cout<<"No"<<endl;
}
乘法逆元[]
求x的乘法逆元
若a,b不互素,则a必然不存在模b的逆元。
0没有乘法逆元
费马小定理
如果p是一个质数,而整数a不是p的倍数,则有a(p-1)===1(modp),则a*a(p-1)===1(modp)。
注意:乘法逆元不一定是存在的。a 存在乘法逆元的充要条件是 a 与模数 p 互质。当模数 p 为质数时,a^(p−2) 即为 a 的乘法逆元。
ll qmi(ll x,ll n,ll p){
ll res=1;
while(n){
if(n&1) res=res*x%p;
x=x*x%p;
n>>=1;
}
return res;
}
ll gcd(ll x,ll y){
return y?gcd(y,x%y):x;
}
void solve(){
ll a,p;
cin>>a>>p;
if(gcd(a,p)==1) cout<<qmi(a,p-2,p)<<endl;
else cout<<"impossible"<<endl;
}
扩展欧几里得
对于 modp 比较大的时候效率更好
拓欧求解 线性同余方程axc(mod b) 的c1的情况,可以转化为解ax+b*y=1这个方程。
当a与p互质,但p不是质数的时候也可以使用。
对于三个自然数a,b,c,求解ax+by=c的(x,y)的整数解。
首先我们要判断是否存在解,对于这个存在整数解的充分条件是 gcd(a,b) | c ,也就是说 c为 a,b 最大公因数的一个倍数。
【c++】
void exgcd(ll a,ll b, ll &x,ll &y){
if(!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll a,p;
void solve(){
ll x,y;
exgcd(a,p,x,y);
x=(x%p+p)%p;
cout<<x<<endl;
}
【python】
def exgcd(a, b):
if b == 0:
return a, 1, 0
else:
gcd, x1, y1 = exgcd(b, a % b)
x = y1
y = x1 - (a // b) * y1
return gcd, x, y
线性求[1,n]的乘法逆元
1≤n≤3×10^6,n<p<20000528。
公式:
int inv[3000010];
int n,p;
void solve(){
cin>>n>>p;
inv[1]=1;
for(int i=2;i<=n;i++){
inv[i]=((ll)p-p/i)*inv[p%i]%p;
}
for(int i=1;i<=n;i++) cout<<inv[i]<<endl;
}
线性求[1,n]的阶乘逆元
我们可以考虑用费马小定理先求出最大那个阶乘的逆元,然后再往回推。
逆元就可一看做是求倒数.
公式:
const int N=1000010;
void init() {
fact[0] = 1;
for (int i = 1; i <N; i++) {
fact[i] = fact[i - 1] * i %mod;
}
inv[N - 1] = power(fact[N- 1], mod - 2);
for (int i = N-2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) %mod;
}
}
模数两两互质的线性同余方程组(CRT)
[problem description]
设正整数两两互素,则同余方程组:
有整数解,并求x的最小非负整数解。
在模下的解是唯一的,解为:
。
[solved]
1.计算所有模数的积M
2.计算第i个方程的
3.计算在模意义下的逆元(的乘法逆元一定是存在的,因为在第2步以及把mi除去了,剩下的一定与mi互质)
4.
const int N=110;
ll r[N];
ll m[N];
//扩展欧几里德
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1,y=0;
return a;
}
ll d=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return d;
}
void solve(){
int n;
cin>>n;
ll M=1;
for(int i=1;i<=n;i++){
cin>>m[i]>>r[i];//m:模数,r:余数
M*=m[i];
}
ll res=0;
for(int i=1;i<=n;i++){
ll c=M/m[i],x=0,y=0;
ll c1=exgcd(c,m[i],x,y);
res=(res+r[i]*c*x%M)%M;
}
cout<<(res%M+M)%M<<endl;
}
模数不一定两两互质的线性同余方程组(EXCRT)
[problem description]
设正整数不一定两两互素,则同余方程组:
如果有整数解,并求x的最小非负整数解。
[solved]
假设有一个有两个方程,我们将两个方程等价合并为一个方程,
其中,
(1)将两个方程转化为不定方程:,则。
(2)求
当时,无解
当时,有解
(3)通过扩欧算法求特解
特解:,
通解:,
(4)合并方程
LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){
x=1,y=0;
return a;
}
LL d=exgcd(b,a%b,y,x);
y -= (a/b) * x;
return d;
}
LL EXCRT(LL m[],LL r[]){
LL m1,m2,r1,r2,p,q;
m1=m[1],r1=r[1];
for(int i=2;i<=n;i++){
m2=m[i],r2=r[i];
LL d = exgcd(m1,m2,p,q);
if((r2-r1)%d){
return -1;
}
p=p*(r2-r1)/d;//特解
p=(p%(m2/d)+m2/d)%(m2/d);//将p转化为最小的非负整数
r1=m1*p+r1;
m1=m1*m2/d;
}
return (r1%m1+m1)%m1;
}
分数取模
[problem desecription]
计算a/b%M
[solved]
费马小定理
[公式推导]
const ll mod=1e9+7;
ll qmi(ll a,ll b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll frac(ll a,ll b){
return ((a%mod)*qmi(b,mod-2))%mod;
}
有理数取余
[problem description]
给出一个有理数c=a/b,求的值。
这个值被定义为 的解。
,,且 a,b 不同时是19260817 的倍数。
[solved]
b%mod*x==a%mod
当b%mod0时,(1)a%mod0时,x有无数解。(2)a%mod!=0时,x无解。
当a%mod!=0时,因为mod是质数,所以mod与b互质,这就满足了费马小定理的前提,所以根据费马小定理变形得必存在x使上述式子成立,即有解。
const int mod=19260817;
void read(int& x){
int f=1;
char ch;
ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
x%=mod;
ch=getchar();
}
x=x*f;
}
ll qmi(int a,int b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=(1ll)*a*a%mod;
b>>=1;
}
return res;
}
void solve(){
int a=0,b=0;
read(a),read(b);
if(b==0) {printf("Angry!\n");}
else printf("%lld\n",(1ll)*a*qmi(b,mod-2)%mod);
}
可爱的质数
[problem description]
给定一个质数 ,以及一个整数 ,一个整数 ,现在要求你计算一个最小的非负整数 ,满足 。
[input]
仅一行,有 个整数,依次代表 。
[output]
仅一行,如果有 满足该要求,输出最小的 ,否则输出 no solution
。
[datas]
[solved]
BSGS算法求解高次同余方程的模板题
ll a,b,p;
ll res;
ll bsgs(ll a,ll b,ll p){
a%=p;
b%=p;
if(b==1) return 0;
ll m=ceil(sqrt(p));//向上取整
ll t=b;
unordered_map<int,int> up;
up[b]=0;
for(int j=1;j<m;j++){
t=t*a%p;//求b*a^j
up[t]=j;
}
ll x=1;
for(int i=1;i<=m;i++)
x=x*a%p;//求a^m
t=1;
for(int i=1;i<=m;i++){
t=t*x%p;
if(up.count(t)) return i*m-up[t];
}
return -1;
}
void solve() {
cin>>p>>a>>b;
res=bsgs(a,b,p);
if(res==-1) cout<<"no solution"<<endl;
else cout<<res<<endl;
}
扩展 BSGS
[problem description]
给定 ,求满足 的最小自然数 。
[input]
每个测试文件中包含若干组测试数据,保证 。
每组数据中,每行包含 个正整数 。
当 时,表示测试数据读入完全。
[output]
对于每组数据,输出一行。
如果无解,输出 No Solution
,否则输出最小自然数解。
[datas]
或 。
[solved]
同余最短路
应用范围
给定 n个整数,求这n个整数能拼凑出多少的其他整数(n个整数可以重复取)
给定n个整数,求这 n 个整数不能拼凑出的最小(最大)的整数
至少要拼几次才能拼出模 K 余p 的数
同余最短路利用同余来构造一些状态,可以达到优化空间复杂度的目的。
类比 [差分约束]方法,利用同余构造的这些状态可以看作单源最短路中的点。同余最短路的状态转移通常是这样的 ,类似单源最短路中。
本文作者:White_Sheep
本文链接:https://www.cnblogs.com/taotao123456/p/17745782.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步