文章目录
题目大意
给定两个数 a , m a,m a,m ,求满足 a u ≡ u ( m o d m ) a^u \equiv u (mod\ \ m) au≡u(mod m) 的一个解。
( 1 ≤ a , m ≤ 1 0 9 , 0 ≤ u ≤ 1 0 18 ) (1\leq a,m \leq10^9 ,0\leq u\leq 10^{18}) (1≤a,m≤109,0≤u≤1018)
题解
参考了讨论区 https://blog.nowcoder.net/n/576f9463036346f0a0fb04fee50fac75 的方法
求解
考虑使用欧拉定理,考虑 b > = ϕ p b>=\phi_p b>=ϕp的情况。
a u ≡ { a u % ϕ m g c d ( a , u ) = 1 a u % ϕ i + ϕ m g c d ( a , u ) ! = 1 ( m o d m ) a^u\equiv\begin{cases}a^{u\% \phi_m }&gcd(a,u)=1\\a^{u\% \phi_i+\phi_m}& gcd(a,u)!=1\end{cases}(mod \ m) au≡{au%ϕmau%ϕi+ϕmgcd(a,u)=1gcd(a,u)!=1(mod m)
定义 d = u % ϕ m d=u\%\phi_m d=u%ϕm 或 u % ϕ m + ϕ m u\%\phi_m+\phi_m u%ϕm+ϕm 和
k ∗ ϕ p + d = u ( k > = 0 ) k*\phi_p+d=u(k>=0) k∗ϕp+d=u(k>=0)
则原式可以转化为 a d ≡ d + k ∗ ϕ m ( m o d m ) a^d \equiv d+k*\phi_m (mod\ m) ad≡d+k∗ϕm(mod m)
移项可以得到 a d − d ≡ k ∗ ϕ m ( m o d m ) a^d-d\equiv k*\phi_m(mod\ m) ad−d≡k∗ϕm(mod m)
ϕ m ∗ x 1 + m ∗ y 1 ≡ g c d ( ϕ m , m ) ( m o d m ) \phi_m*x1+m*y1\equiv gcd(\phi_m,m) (mod \ m) ϕm∗x1+m∗y1≡gcd(ϕm,m)(mod m) 是一个已知有解的同余方程
回到上一个方程想要得到解 k k k ,显然要满足 a d − d = x ∗ g c d ( m , ϕ m ) , ( x > 0 ) a^d-d=x *gcd(m,\phi_m),(x>0) ad−d=x∗gcd(m,ϕm),(x>0)
也就是 a d ≡ d ( m o d g c d ( m , ϕ m ) ) a^d \equiv d (mod \ gcd(m,\phi_m)) ad≡d(mod gcd(m,ϕm))。
重新得到了题目,但是模数缩小了,因此我们想到了递归,直到模数为 1 1 1 时直接推出答案。
回溯
假设我们已经得到了最后一组解 d = 0 d=0 d=0 ,
求解同余方程 a d − d ≡ k ∗ ϕ m ( m o d m ) a^d-d\equiv k*\phi_m(mod\ m) ad−d≡k∗ϕm(mod m),使用扩展欧几里得定理,推出 x 1 x1 x1 的值,
k = x 1 ∗ a d − d g c d ( m , ϕ m ) % m o d k=x1*\frac{a^d-d}{gcd(m,\phi_m)}\%mod k=x1∗gcd(m,ϕm)ad−d%mod
由于 a d a^d ad 超出范围,根据 a b % ( b ∗ c ) = a % ( b ∗ c ) b \frac{a}{b}\%(b*c)=\frac{a\%(b*c)}{b} ba%(b∗c)=ba%(b∗c)得出
k = x 1 ∗ ( a d − d ) % m / ϕ m k=x1*(a^d-d)\%m/\phi_m k=x1∗(ad−d)%m/ϕm 。
再利用 k ∗ ϕ p + d = u k*\phi_p+d=u k∗ϕp+d=u,得出结果即可。
参考代码
cpp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll phi(ll x)
{
ll ans=x;
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
ans=ans/i*(i-1);
while(x%i==0)
x/=i;
}
if(x!=1)
ans=ans/x*(x-1);
return ans;
}
ll ksm(ll a,ll b,ll p)
{
ll res=1;
while(b)
{
if(b&1)
res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1,y=0;
return a;
}
ll k=exgcd(b,a%b,y,x);
y-=a/b*x;
return k;
}
int n,T;
ll a,m;
ll work(ll a,ll p) //递归求解
{
if(p==1)
return 0;
ll m=phi(p);
ll b=work(a,__gcd(m,p))+m;
ll x,y;
ll d=exgcd(m,p,x,y);
ll k=(((x*(ksm(a,b,p)-b+p))%p+p)%p/d); //回溯求值
return k*m+b;
}
int main()
{
cin>>T;
while(T--)
{
scanf("%lld%lld",&a,&m);
printf("%lld\n",work(a,m));
}
}