题意:给定
a,b,p,x,求n∗an≡b(modp)
a
,
b
,
p
,
x
,
求
n
∗
a
n
≡
b
(
m
o
d
p
)
可以设
n=i∗(p−1)+j
n
=
i
∗
(
p
−
1
)
+
j
,那么原式就等于
(i∗(p−1)+j)∗ai∗(p−1)+j≡b(modp)
(
i
∗
(
p
−
1
)
+
j
)
∗
a
i
∗
(
p
−
1
)
+
j
≡
b
(
m
o
d
p
)
根据费马小定理
ap−1≡1(modp)
a
p
−
1
≡
1
(
m
o
d
p
)
,就可以化简为
j−i=b/aj,也就是i=j−b/aj
j
−
i
=
b
/
a
j
,
也
就
是
i
=
j
−
b
/
a
j
(这里的除法可以用乘法逆元来求),那么我们就可以通过枚举
j
j
来确定的值,也就可以确定
n
n
的值了。
然后来讨论循环节的问题,对于任意一个数,在
x<p
x
<
p
的时候,x取任何值,进行
x%p
x
%
p
操作之后的值都是不同的,然而当
x>=p
x
>=
p
之后就会和
x<p
x
<
p
的取模之后的值重复,这里就把
p
p
叫作的循环节。
那么
n%p
n
%
p
的循环节就是p了,那么根据费马小定理,我们也可以得出a^n\%p的循环节是(p-1),那么对于原式左边得循环节就是p*(p-1),也就是说,在
p∗(p−1)
p
∗
(
p
−
1
)
的范围内,原式左边只会出现一个可行解。那可以令
P=p∗(p−1)
P
=
p
∗
(
p
−
1
)
,然后直接用所给的边界
x/P
x
/
P
,然后在考虑x%P的部分与所求出n的关系就可以了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
long long mod,ans;
long long quickpow(long long a,long long b)
{
if(b<0)return 0;
long long ret=1;
a%=mod;
while(b)
{
if(b&1)
ret=(ret*a)%mod;
b>>=1;
a=(a*a)%mod;
}
return ret;
}
long long inv(long long a)
{
return quickpow(a,mod-2);
}
int main()
{
long long a,b,p,x;
while(~scanf("%lld %lld %lld %lld",&a,&b,&p,&x))
{
mod = p;
ans = 0;
long long P = p * (p - 1);
for (long long j = 0; j < p - 1; j++)
{
long long temp = b * inv(quickpow(a,j)) % p;
long long i = (j - temp + p) % p;
long long n = (i * (p - 1) + j)%P;
if(n == 0)n = P;
ans += x/P;
if(x % P >= n)
{
ans++;
}
}
printf("%lld\n",ans);
}
}