文章目錄
- 題目大意
- 題解
- 求解
- 回溯
- 參考代碼
題目大意
給定兩個數 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%?m?au%?i?+?m??gcd(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,得出結果即可。
參考代碼
#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));}
}