余数定理学习指南
前置芝士
乘法逆元
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,则 \(a∗r_i+b (i∈[0,n−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,则 \(a∗r_i\)也构成一个模 n 的简化剩余系。
[证明]
因为(a,m)=1,(x,m)=1,于是(ax,m)=1。故ax为m的简化剩余类的剩余。
因为φ(n)是固定不变的,只要通过反证法证明\(ax_{1},ax_{2},\ldots,ax_{\phi(m)}\)这\(\phi(m)\)个数模m两两不同余即可。若存在\(x_i\)和\(x_j\),i!=j,使得\(ax_i\equiv ax_j(modm)\)。
若上面等式成立,需要满足\(m | a(x_i-x_j)\),由于gcd(a,m)=1,所以\(x_i \equiv x_j(modm)\),这与\(x_{1},x_{2},\ldots,x_{\phi(m)}\)模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
但万幸的是,若\(b\neq0\),扩展欧几里得算法求出的可行解必有\(|x|\leq b,|y|\leq a\)。
// 求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互质的数的数目。
公式:\(\phi(x)=x(1-\frac1{p_1})(1-\frac1{p_2})\cdots(1-\frac1{p_n})\text{,其中 }p_1,p_2\cdots p_n\text{ 是 }x\text{ 的所有质因数。}\)
[性质]
(1)积性函数,但不完全积性函数
当n,m互质时,满足\(ϕ(nm)=ϕ(n)*ϕ(m)\)
当n根据算术基本定理分解为\(n=p_1^{c1}p_2^{c2}\ldots p_m^{cm}\)时,\(\phi(n)=\prod_{i=1}^m\phi(p_i^{ci})\)
【证明】
若n与m互质,则n与m没有相同的质因子设n的质因子个数为\(cnt_n\),m的质因子个数为\(cnt_m\),则
(2)对于质数p,它的欧拉函数值ϕ(p)=p−1
[证明]
因为p为质数,所以比它小的数都和它互质,即在1~p中共有p-1个数和它互质。
(3)当n为奇数时,ϕ(2∗n)=ϕ(n)
[证明]
当n为奇数时,n与2互质,由欧拉函数是积性函数可知,n与2互质时,\(\phi(2n)=\phi(2)*\phi(n)\)
又因为ϕ(2)=1,所以ϕ(2n)=ϕ(n).
(4)当\(n=p^k\),\(\phi(n)=p^k-p^{k-1}\)
(5)n中与n互质的数的和为\(\phi(n)\div2\times n(n>1)\),并且ϕ(n)(n>2)为偶数。
(6)\(\text{若}p|n\text{且}p^2|n\text{,则}\phi(n)=\phi(\frac np)*p\),\(\text{若}p|n\text{且}p^2\nmid n,\text{则}\phi(n)=\phi(\frac np)*(p-1)\)
(7)\(\sum_{d|n}\phi(d)=n\)
(8)\(\phi(n)=\sum_{d|n}\frac{\mu(d)}d\)
欧拉定理
[定义]
\(\text{若}\gcd(a,m)=1\text{,则 }a^{\varphi(m)}\equiv1\mathrm{~(mod~}m)。\)
费马小定理\(a^{p-1}\equiv1\textit{ ( mod p)}\),其中gcd(a,p)=1,是欧拉定理的一种特性。
[证明]
。。。。。。
扩展欧拉定理
[公式]
[证明]
。。。。。。
快速幂
递归方式:log(n)
【思路分析】
求\(x^{n}\): ①若n为偶数,\(x^{n} = x^{n/2} *x^{n/2} ∗ x^{(n/2)*2}\); ②若n为奇数,\(x^{n}=x^{n/2}*x^{n/2}*x\)。
//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)
【思路分析】
求\(x^{11}\): 11的二进制:1011,于是\(x^{11}= x^{(2^0 + 2^1 + 2^3)}=x^{( 2^0 )}*x^{(2^1)}*x^{(2^3)}x= x * x ^{2}*x^{8}\), 只要分别求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
\({-2}^{31}<=n<=2^{31}-1\)
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算法
给定一个质数 \(p\),以及一个整数 \(a\),一个整数 \(b\),现在要求你计算一个最小的非负整数 \(x\),满足 \(a^x \equiv b \pmod p\)。
Baby Step Giant Step
(1)由扩展欧拉定理\(a^{x}\equiv a^{xmod\varphi(p)}(modp)\),可知\(a^x\)模p意义下的最小循环节为\(\varphi({p})\),因\(\varphi(p)<{p}\),故考虑\(x\in[0,p]\),必能找到最小整数x。
(2)暴力枚举的时间复杂度为O(p)的。
(3)令\(x=im-j\),其中\(m=\lceil\sqrt{p}\rceil,i\in[1,m],j\in[0,m-1]\)
则\(a^{im-j}\equiv b(modp)\),转化为\((a^{m})^{i}\equiv ba^{j}(modp)\)。
(4)先枚举j,把(ba^j,j)丢进哈希表,如果key重复,用更大的j代替旧值。
(5)再枚举i,计算\((a^m)^i\),在哈希表中查找是否又相等的key,找到第一个即结束,则最小的x=im-j。
扩展BSGS算法
给定 \(a,p,b\),求满足 \(a^x≡b \pmod p\) 的最小自然数 \(x\) 。
[解题步骤]
(1)当a,p互质时,可以使用BSGS算法求解。
(2)当a,p不互质时,想办法让他们变得互质。
(3)原方程等价于\(a*a^{x-1}+py=b\)
设\(d_1=gcd(a,p),如果\)\(d_{1}\nmid b\),则原方程无解。
否则方程两边同时除于\(d_1\),得\(\frac{a}{d_{1}}a^{x-1}\equiv\frac{b}{d_{1}}\left(mod\frac{p}{d_{1}}\right)\)
如果a和\(\frac p{d_1}\)仍不互质再除
设\(d_2=\gcd(\mathrm{a},\frac{p}{d_1})\),\(d_{2}\nmid \frac{b}{d1}\),则原方程无解。
或者方程两边需要同时除于\(d_2\),得\(\frac{a^{2}}{d_{1}d_{2}}a^{x-2}\equiv\frac{b}{d_{1}d_{2}}\left(mod\frac{p}{d_{1}d_{2}}\right)\)
不停的判断下去,直到\(a\perp\frac{p}{d_{1}\cdots d_{k}}\),记\(D=\prod_{i=1}^{k}d_{i}\)
原方程变为\(\frac{a^{k}}{D}a^{x-k}\equiv\frac{b}{D}\left({\operatorname*{mod}}{\frac{p}{D}}\right)\)
因\(a\perp\frac pD\),则\(\frac{a^{k}}{D}\perp\frac{p}{D}\),则\(\frac{a^k}D\)就有逆元了,把它丢到方程右边,就是一个标准的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不互质的数的个数。
(\(1\leq l\leq r\leq10^{12},r-l\leq10^6\))
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),有\(a^n===a^{n\enspace mod(p-1)} \enspace(mod p)\)
欧拉降幂
[problem description]
求\(a^b \enspace mod \enspace m (1\leq a\leq10^9,1\leq b\leq10^{20000000},1\leq m\leq10^8)\)
[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;
}
同余方程[\(\mathrm{ax\equiv b({\mathrm{mod}}\,m)}\)]
不定方程[\(ax+by=gcd(a,b)\)]
[solved]
扩展欧几里得算法实现
当b=0时,ax+by=a=>x=1,y=0
当b!=0时,gcd(a,b)=gcd(b,a%b)
[公式推导]
所以\(x=y1,y=x1-\frac{a}{b}\times y1\)
可以通过递归算法,先求出下一层的\(x_1\),\(y1\),再回代到上一层,层层回代,可求特解\((x_0,y_0)\)。
通解:
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;
}
不定方程[\(ax+by=c\)]
[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;
}
乘法逆元[\(\mathrm{ax\equiv 1({\mathrm{mod}}\, m)}\)]
求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。
公式:\(\mathrm{inv[i]=((p-\lfloor\frac pi\rfloor)\times inv[p\%i])\%p}\)
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]的阶乘逆元
我们可以考虑用费马小定理先求出最大那个阶乘的逆元,然后再往回推。
逆元就可一看做是求倒数.
公式:\(\begin{aligned}\frac{1}{(n+1)!}\times(n+1)=\frac{1}{n!}\end{aligned}\)
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]
设正整数\(m_1,m_2,...,m_k\)两两互素,则同余方程组:
有整数解,并求x的最小非负整数解。
在模\(M = m 1 ∗ m 2 ∗ . . . ∗ m_k\)下的解是唯一的,解为:
\(\mathrm x\equiv(\mathrm a_1\mathrm c_1\mathrm c_1^{-1}+\mathrm a_2\mathrm c_2\mathrm c_2^{-1}+...+\mathrm a_k\mathrm c_k\mathrm c_k^{-1})\bmod M\)。
[solved]
1.计算所有模数的积M
2.计算第i个方程的\(c_i=\dfrac{M}{m_i}\)
3.计算\(c_i\)在模\(m_i\)意义下的逆元\(c_i^{-1}\)(\(c_i\)的乘法逆元一定是存在的,因为在第2步以及把mi除去了,剩下的一定与mi互质)
4.\(x=\sum_{i=1}^{n}r_{i}c_{i}c_{i}^{-1}(modM)\)
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]
设正整数\(m_1,m_2,...,m_k\)不一定两两互素,则同余方程组:
如果有整数解,并求x的最小非负整数解。
[solved]
假设有一个有两个方程,我们将两个方程等价合并为一个方程\(\mathrm{x\equiv r(mod\quad m)}\),
其中\(r=m_1p+r_1\),\(m=lcm(m_1,m_2)\)
(1)将两个方程转化为不定方程:\(x=m_1p+r_1=m_2q+r_2\),则\(m_1p-m_2q=r_2-r_1\)。
(2)求\(gcd(m_1,m_2)\)
当\(\gcd(m_{1},m_{2})\nmid(r_{2}-r_{1})\)时,无解
当\(\gcd(m_{1},m_{2})\mid(r_{2}-r_{1})\)时,有解
(3)通过扩欧算法求特解
特解:\(p=p*\frac{r_{2}-r_{1}}{gcd(m_1,m_2)}\),\(q=q*\frac{r_{2}-r_{1}}{gcd(m_1,m_2)}\)
通解:\(P=p+\frac{m_{2}}{gcd(m_1,m_2)}*k\),\(Q=q-\frac{m_{1}}{gcd(m_1,m_2)}*k\)
(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,求$cmod 19260817 $的值。
这个值被定义为\(bx≡a(mod19260817)\) 的解。
\(0≤a≤10^{10001}\),\(1≤b≤10^{10001}\),且 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]
给定一个质数 \(p\),以及一个整数 \(a\),一个整数 \(b\),现在要求你计算一个最小的非负整数 \(x\),满足 \(a^x \equiv b \pmod p\)。
[input]
仅一行,有 \(3\) 个整数,依次代表 \(p, a, b\)。
[output]
仅一行,如果有 \(l\) 满足该要求,输出最小的 \(l\),否则输出 no solution
。
[datas]
\(2\le a,b < p<2^{31}\)
[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]
给定 \(a,p,b\),求满足 \(a^x≡b \pmod p\) 的最小自然数 \(x\) 。
[input]
每个测试文件中包含若干组测试数据,保证 \(\sum \sqrt p\le 5\times 10^6\)。
每组数据中,每行包含 \(3\) 个正整数 \(a,p,b\) 。
当 \(a=p=b=0\) 时,表示测试数据读入完全。
[output]
对于每组数据,输出一行。
如果无解,输出 No Solution
,否则输出最小自然数解。
[datas]
\(1\le a,p,b≤10^9\) 或 \(a=p=b=0\)。
[solved]
同余最短路
应用范围
给定 n个整数,求这n个整数能拼凑出多少的其他整数(n个整数可以重复取)
给定n个整数,求这 n 个整数不能拼凑出的最小(最大)的整数
至少要拼几次才能拼出模 K 余p 的数
同余最短路利用同余来构造一些状态,可以达到优化空间复杂度的目的。
类比 [差分约束]方法,利用同余构造的这些状态可以看作单源最短路中的点。同余最短路的状态转移通常是这样的 \(f(i+y)= f(i) + y)\),类似单源最短路中\(f(v)= f(u) +edge(u,v)\)。