同余学习笔记
定义
若整数 (a) 和整数 (b) 除以正整数 (m) 的余数相等,则称 (a,b) 模 (m) 同余,记为 (aequiv b (mod m)) 。
同余系与剩余系
对于 (forall ain[0,m-1]) ,集合 {(a+km)} ((kinN)) 的所有数模 (m) 同余,余数都是 (a) 。该集合称为一个模 (m) 的同余类,简记为 (ar{a}) 。
模 (m) 的同余类一共有 (m) 个,分别为 (ar{0},ar{1},cdots,overline{m-1}) 。它们构成 (m) 的完全剩余系。
(1sim m) 中与 (m) 互质的数代表的同余类共有 (varphi(m)) 个,它们构成 (m) 的简化剩余系。
简化剩余系关于模 (m) 乘法封闭。这是因为若 (a,b(1leq a,bleq m)) 与 (m) 互质,则 (a*b) 也不可能与 (m) 有相同的质因子,即 (gcd(a*b,m)=1) 。再由余数的定义可知 (a*b mod m) 也与 (m) 互质,即 (a*b mod m) 也属于 (m) 的简化剩余系。
欧拉定理
费马小定理
若 (p) 是质数,则对于任意整数 (a) ,有 (a^pequiv a (mod p)) 。
欧拉定理
若正整数 (a,n) 互质,则 (a^{varphi(n)}equiv 1 (mod n)) 。
证明:
设 (n) 的简化剩余系为 {(overline{a_1},overline{a_2},cdots,overline{a_{varphi(n)}})}。对 (forall a_i,a_j) ,若 (a*a_iequiv a*a_j (mod n)) ,则 (a*(a_i-a_j)equiv 0) 。因为 (a,n) 互质,所以 (a_i-a_jequiv 0) ,即 (a_iequiv a_j) 。故当 (a_i ot= a_j) 时, (aa_i,aa_j) 也代表不同的同余类。
又因为简化同余系关于模 (n) 乘法封闭,故 (overline{aa_i}) 也在简化剩余系中。因此,集合 {(overline{a_1},overline{a_2},cdots,overline{a_{varphi(n)}})}与集合 {(overline{aa_1},overline{aa_2},cdots,overline{aa_{varphi(n)}})}都能表示 (n) 的简化剩余系。综上所述: (a^{varphi(n)}a_1a_2cdots a_{varphi(n)}equiv(aa_1)(aa_2)cdots(aa_{varphi(n)})equiv a_1a_2cdots a_{varphi(n)} (mod n))
因此 (a^{varphi(n)}equiv1 (mod n)) 。
当 (p) 是质数时, (varphi(n)=n-1) ,并且只有 (p) 的倍数与 (p) 不互质。所以,只要 (a) 不是 (p) 的倍数,就有 (a^{p-1}equiv1 (mod p)) ,两边同乘 (a) 就是费马小定理。另外,若 (a) 是 (p) 的倍数,费马小定理显然成立。
证毕。
欧拉定理的推论
若正整数 (a,n) 互质,则对于任意正整数 (b) ,有 (a^bequiv a^{b (mod varphi(n))} (mod n)) 。
对于一些计数类题目,如果要计算乘方算式,根据欧拉定理,可以先把底数对 (p) 取模、指数对 (varphi(n)) 取模。再计算乘方。
特别的,当 (a,n) 不一定互质且 (b>varphi(n)) 时,有 (a^bequiv a^{b mod varphi(n)+varphi(n)} (mod n)) 。这意味着即使底数和模数不互质,我们也有办法把指数的规模缩小到容易计算的范围内。上式可以通过寻找 (a^b mod n) 的指数循环节证明。
例题
题面
Solution
(x) 个 (8) 连在一起组成的正整数可以写成 (8*(10^x-1)/9) 。题目要求一个最小的 (x) 满足 (L|8*(10^x-1)/9) 。
设 (gcd(8,L)=d) ,则 (frac{L}{d}|frac{8}{d}(10^x-1)/9) 。由于 (frac{L}{8}) 与 (frac{8}{d}) 互质,所以 (frac{L}{d}|(10^x-1)/9) 。整理得到 (frac{9L}{d}|10^x-1) 。写成同余的形式 (10^xequiv 1 (mod frac{9L}{d})) 。
写到这里我们想到欧拉定理的形式。
- 对于 (gcd(10,frac{9L}{d})!=1) ,方程无解。
证明:以上方程等价于 (10^x=m*frac{9L}{d}+1) ,所以 (gcd(10^x,frac{9l}{d})=gcd(m*frac{9l}{d}+1,frac{9l}{d})=gcd(1,frac{9l}{d})=1) 。
- 若 (a,n) 互质,则满足 (a^xequiv 1 (mod n)) 的最小正整数解 (x_0) 一定是 (varphi(n)) 的约数。
证明:假设 (x_0 ot| varphi(n)) ,则 (varphi(n)=m*x_0+r (0<r<x_0)) 。因为 (a^{varphi(n)}equiv 1 (mod n)) ,那么 (a^{m*x_0+r}equiv 1 (mod n)) 。因为 (a^{x_0}equiv 1 (mod n)) ,那么对上式化简得 (a^requiv 1 (mod n)) 。这与 (x_0) 最小矛盾,假设不成立。原命题成立。
因此我们只需要求出 (varphi(frac{9l}{d})) ,然后枚举它的约数,用快速幂判断即可。
时间复杂度: (o(sqrt{L} log L)) 。
Code
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<cstring>
#include<stdio.h>
#include<iostream>
#include<algorithm>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
ill qpow(ll x,ll m,ll mod){
ll res=1,bas=x%mod;
while(m){
if(m&1) res=mul(res,bas,mod);
bas=mul(bas,bas,mod),m>>=1;
}
return res%mod;
}
ll n,cnt;
ill gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
ill phi(ll x){
rl res=x;
for(ri i=2;i*i<=x;++i)
if(x%i==0){
res=res/i*(i-1);
while(x%i==0) x/=i;
}
if(x>1) res=res/x*(x-1);
return res;
}
il solve(ll n){
rl d=gcd(8,n);
rl tmp=9*n/d;
if(gcd(tmp,10)!=1) printf("Case %lld: 0
",++cnt);
else{
rl p=phi(tmp);
for(ri i=1;i<=sqrt(p);++i)
if(p%i==0&&qpow(10,i,tmp)==1){
printf("Case %lld: %d
",++cnt,i);
return ;
}
for(ri i=sqrt(p);i>1;--i)
if(p%i==0&&qpow(10,p/i,tmp)==1){
printf("Case %lld: %d
",++cnt,p/i);
return ;
}
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
while(scanf("%lld",&n)!=EOF){
if(!n) return 0;
solve(n);
}
return 0;
}
拓展欧几里得算法
Bezout定理
对于任意整数 (a,b) ,一定存在一对整数 (x,y) 满足 (ax+by=gcd(a,b)) 。
证明:
在欧几里得算法的最后一步,即 (b=0) 时,显然有一对整数 (x=1,y=0) ,使得 (a*1+0*0=gcd(a,0)) 。
若 (b>0) ,则 (gcd(a,b)=gcd(b,a\%b)) 。
假设存在一对整数 (x,y) ,满足 (b*x+(a\%b)*y=gcd(b,a\%b)) 。
因为 (bx+(a\%b)y=bx+(a-blfloor a/b floor)y=ay+b(x-lfloor a/b floor y)) ,所以令 (x^{'}=y,y^{'}=x-lfloor a/b floor y) ,就得到了 (ax^{'}+by^{'}=gcd(a,b)) 。
对欧几里得算法的递归过程应用数学归纳法,可知定理成立。
Bezout定理是按照欧几里得算法的思路证明的,且上述证明的同时给出了整数 (x,y) 的计算方法。这种计算方法被称作拓展欧几里得算法。
int exgcd(int a,int b,int &x,int &y){
if(b==0){x=1,y=0;return a;}
int d=exgcd(b,a%b,x,y);
int z=x;x=y,y=z-y*(a/b);
return d;
}
上述程序求出方程 (ax+by=gcd(a,b)) 的一组特解 (x_0,y_0) ,并返回 (a,b) 的最大公约数 (d) 。
对于更一般的方程 (ax+by=c) ,它有解当且仅当 (d|c,d=gcd(a,b)) 。我们可以先求出 (ax+by=d) 的一组特解 (x_0,y_0) ,然后令 (x_0,y_0) 同时乘上 (frac{c}{d}) ,就得到了 (ax+by=c) 的一组特解 (frac{c}{d}x_0,frac{c}{d}y_0) 。
事实上,方程 (ax+by=c) 的通解可以表示为: (x=frac{c}{d}x_0+kfrac{b}{d},y=frac{c}{d}y_0+kfrac{a}{d},kinN) 。
例题
题面
Solution
假设要跳 (x) 次才能相遇,则题目等价于求一个最小的 (x) ,使得 (a+nxequiv b+mx (mod L)) 。
整理式子。
((n-m)xequiv b-a (mod L))
将同余号去掉,得 (x(n-m)+kL=b-a) 。其中 (n-m,L,b-a) 均为已知量,所以可以用 (exgcd) 求解。
注意到 (n-m) 的符号不确定,所以在做题之前进行一下处理,先将 (n-m) 变为正数,再将 (b-a) 变为正数,最后再转换为二元一次方程求解。(注意要开 (long long) )
Code
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il print(T x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(res*bas)%mod;
bas=(bas*bas)%mod,m>>=1;
}
return res%mod;
}
ll a,b,c,n,m,l,x,y;
ill gcd(ll x,ll y){return y==0?x:gcd(y,x%y);}
ill exgcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return a;}
rl d=exgcd(b,a%b,x,y);
rl z=x;x=y,y=z-(a/b)*y;
return d;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(a),read(b),read(n),read(m),read(l);
c=b-a,a=n-m,b=l;
if(a<0) a=-a,c=-c,c=(c%l+l)%l;
rl d=gcd(a,b);
if(c%d!=0) printf("Impossible");
else{
exgcd(a,b,x,y);
x=1ll*x*c/d;ri mod=b/d;
printf("%lld",(x%mod+mod)%mod);
}
return 0;
}
乘法逆元
若整数 (b,m) 互质,并且 (b|a) ,则存在一个整数 (x) ,使得 (a/bequiv a*x (mod m)) 。称(x) 为 (b) 的模 (m) 的乘法逆元。记为 (b^{-1}(mod m)) 。
因为 (a/bequiv a*b^{-1}equiv a/b*b*b^{-1}(mod m)) ,所以 (b*b^{-1}equiv 1(mod m)) 。
如果 (m) 为质数(此时用 (p) 代替 (m) ),并且 (b<p) ,根据费马小定理, (b^{p-1}equiv1(mod p)) ,即 (b*b^{p-2}equiv1(mod p)) 。因此,当模数 (p) 为质数时, (b^{p-2}) 即为 (b) 的乘法逆元。
如果只是保证 (b,m) 互质,那么乘法逆元可通过求解同余方程 (b*xequiv 1(mod m))得到
运用乘法逆元,我们在计数类问题中遇到 (a/b) 这样的除法算式,也可以先把 (a,b) 各自对模数 (p) 取模,再计算 (a*b^{-1}(mod p)) 作为最终结果。当然,前提是必须保证 (b,p) 互质。
线性同余方程
给定正数 (a,b,m) ,求一个整数 (x) 满足 (a*xequiv b(mod m)) ,或者给出无解。因为未知数的指数为 (1) ,所以我们称之为一次同余方程,也称为线性同余方程。
(a*xequiv b(mod m)) 等价于 (a*x-b) 是 (m) 的倍数,不放设为 (-y) 倍。于是该方程可以改写为 (a*x+m*y=b) 。
线性同余方程有解当且仅当 (gcd(a,m)|b) 。
中国剩余定理
设 (m_1,m_2,cdots,m_n) 是两两互质的整数, (m=prodlimits_{i=1}^n m_i) , (M_i=m/m_i) , (t_i) 是线性同余方程 (M_it_iequiv 1(mod m_i)) 的一个解。对于任意的 (n) 个整数 (a_1,a_2,cdots,a_n) ,方程组 (left{egin{matrix} xequiv a_1(mod m_1)\ xequiv a_2(mod m_2)\ cdots\ xequiv a_2(mod m_3)\ end{matrix} ight.) 有整数解,解为 (x=sumlimits_{i=1}^n a_i M_i t_i) 。
证明:
因为 (M_i=M/m_i) ,是除 (m_i) 外所有模数的倍数,所以 (forall k ot= i,a_i M_i t_iequiv 0(mod m_k)) 。又因为 (a_iM_it_iequiv a_i(mod m_i)) ,所以带入 (x=sumlimits_{i=1}^n a_i M_i t_i) ,原方程组成立。