题面
https://www.luogu.com.cn/problem/P2485
题解
对于T=1,快速幂即可。
对于T=2,exgcd即可。
重点是T=3,需要使用BSGS算法。
BSGS算法
用来快速求解形如\({a^x}{\equiv}r(mod p)\)的指数同余方程的算法,适用范围是\(\gcd(a,p)=1\)。
由欧拉-费马定理可得\(a^{\phi(p)}{\equiv}1(mod p)\)。下设\(0<=x<{\phi(p)}\)。
设置“间隔”\(T\),那么x一定可以表示成\(iT-j\)(其中\(1<=j<=T\))的形式。下有
\[a^{iT-j}{\equiv}r{\mod p}\]
\[a^{iT}{\equiv}r*a^j{\mod p}\]
故只要某一个\(a^{iT}(1<=i<={\lceil{{\phi(p)-1}\over{T}}\rceil})\)以及某一个\(r*a^j(1<=j<=T)\)相等,就得到了一个解\(x=iT-j\)。
实现时只需要将所有的\(r*a^j\)预处理好放入Hash表内,在计算\(a^{iT}\)的时候同步查询就可以了。
计算\(r*a^j\)的时间复杂度为\(O(T)\),计算\(a^{iT}\)的时间复杂度是\(O({{\phi(p)}\over{T}})\),取\(T=\sqrt{\phi(p)}\)即可保证总时间复杂度是\(O(\sqrt{\phi(p)})\)。
回本题
给出\(y\),\(z\),\(p\),计算满足\(y^x{\equiv}z{\mod{p}}\)的最小非负整数 \(x\)。
保证\(p\)是质数。
首先考虑\(p|y\)的情况,即\({\gcd}(y,p){\not=}1\)的情况,此时不能适用BSGS算法
- 1、\(z=0\):\(x=1\)即可
- 2、\(z=1\):\(x=0\)即可
- 3、others:无解
其他情况可以适用BSGS解决。
代码
#include
using namespace std;
#define ll long long
#define rg register
inline ll read(){
ll s = 0,ww = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')ww = -1;ch = getchar();}
while('0' <= ch && ch <= '9'){s = 10 * s + ch - '0';ch = getchar();}
return s * ww;
}
inline void write(ll x){
if(x < 0)putchar('-'),x = -x;
if(x > 9)write(x / 10);
putchar('0' + x % 10);
}
ll mod,g;
inline ll check(ll x){
return (x % mod + mod) % mod;
}
namespace ModCalc{
inline void Inc(ll &x,ll y,ll mod){
x += y;if(x >= mod)x -= mod;
}
inline void Dec(ll &x,ll y,ll mod){
x -= y;if(x < 0)x += mod;
}
inline ll Add(ll x,ll y,ll mod){
Inc(x,y,mod);return x;
}
inline ll Sub(ll x,ll y,ll mod){
Dec(x,y,mod);return x;
}
}
using namespace ModCalc;
inline ll power(ll a,ll n){
ll x = a,s = 1;
while(n){
if(n & 1)s = s * x % mod;
x = x * x % mod;
n >>= 1;
}
return s;
}
inline ll exgcd(ll a,ll &x,ll b,ll &y){
if(!b){
x = 1,y = 0;
return a;
}
ll g = exgcd(b,y,a%b,x);
y -= a / b * x;
return g;
}
mapM;
int main(){
ll T = read(),opt = read();
if(opt == 1){
while(T--){
ll a = read(),n = read();
mod = read();a %= mod;
write(power(a,n));putchar('\n');
}
}
else if(opt == 2){
while(T--){
ll a = read(),r = read();
mod = read();
ll x,y;
ll g = exgcd(a,x,mod,y);
if(r % g != 0){
puts("Orz, I cannot find x!");
continue;
}
a /= g,r /= g,mod /= g;
x = check(x * r);
write(x);putchar('\n');
}
}
else{
while(T--){
ll a = read(),r = read();
mod = read();a %= mod,r %= mod;
if(!a){
if(!r)puts("1");
else if(r == 1)puts("0");
else puts("Orz, I cannot find x!");
continue;
}
ll T = (ll)round(sqrt(mod));
ll a_T = power(a,T);
M.clear();
for(rg ll i = 1,cur = r * a % mod;i <= T;i++,cur = cur * a % mod)M[cur] = i;
ll flag = 0;
for(rg ll i = 1,cur = a_T;i * T - T <= mod - 2;i++,cur = cur * a_T % mod)if(M.count(cur)){
write(Sub(i*T%(mod-1),M[cur],mod-1)),putchar('\n');
flag = 1;
break;
}
if(!flag)puts("Orz, I cannot find x!");
}
}
return 0;
}