题意:给出
n
,
x
0
,
a
,
b
,
p
,
且
x
i
=
(
a
∗
x
i
−
1
+
b
)
m
o
d
p
n,x_0,a,b,p,且x_{i}=(a*x_{i-1}+b) mod p
n,x0,a,b,p,且xi=(a∗xi−1+b)modp
多组测试用例,每组测试用例为v,找到最小的i,使得满足
x
i
%
p
=
v
x_i \% p=v
xi%p=v
x
n
=
a
n
∗
x
0
+
a
n
−
1
b
+
a
n
−
2
b
+
.
.
.
+
b
x_{n}=a^n*x_0+a^{n-1}b+a^{n-2}b+...+b
xn=an∗x0+an−1b+an−2b+...+b
即:
x
n
=
a
n
∗
x
+
b
∗
(
1
−
a
n
)
1
−
a
x_n=a^n*x+\frac{b*(1-a^n)}{1-a}
xn=an∗x+1−ab∗(1−an)
a
n
(
a
∗
x
0
−
x
0
+
b
)
≡
(
a
∗
v
−
v
+
b
)
m
o
d
p
a^n(a*x_0-x_0+b)\equiv(a*v-v+b)mod p
an(a∗x0−x0+b)≡(a∗v−v+b)modp
此时看到这个式子,我们就想到了BSGS:
求解关于x的方程:
y
x
≡
z
m
o
d
p
y^x\equiv z modp
yx≡zmodp
因为这里的
g
c
d
(
y
,
p
)
=
1
gcd(y,p)=1
gcd(y,p)=1
我们把
x
x
x写成一个
a
m
−
b
am−b
am−b的形式
那么,原式变成了
y
a
m
≡
z
y
b
(
m
o
d
p
)
y^{am}\equiv zy^b(mod p)
yam≡zyb(modp)
我们求出所有b可能的取值
(
0
m
−
1
)
(0~m-1)
(0 m−1),并且计算右边的值
同时map之类的东西存起来,方便查询
对于左边,我们可以枚举所有可能的a,然后直接查右边的值有没有相等的即可
复杂度是
O
(
m
a
x
(
m
,
p
/
m
)
)
O(max(m,p/m))
O(max(m,p/m))
不难证明
m
=
(
√
p
)
m=(√p)
m=(√p)时复杂度最优
所以bsgs算法的复杂度是 O ( ( √ p ) ) O((√p)) O((√p))
那么此时此题就可化为:
a
x
m
(
a
∗
x
0
−
x
0
+
b
)
≡
a
y
(
a
∗
v
−
v
+
b
)
m
o
d
p
a^{xm}(a*x_0-x_0+b)\equiv a^y(a*v-v+b)mod p
axm(a∗x0−x0+b)≡ay(a∗v−v+b)modp
还有此时我们不能让
m
=
p
m=\sqrt{p}
m=p,因为p可能会很大,因为测试用例有1000个,那么此时我们令
m
=
1001
,
故
x
=
1
e
6
,
y
的
可
能
取
值
为
(
0
,
1001
)
m=1001,故x=1e6,y的可能取值为(0,1001)
m=1001,故x=1e6,y的可能取值为(0,1001)
我们预处理左边的就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int m=1001,x=1e6,y=1001;
unordered_map<LL,LL> ma;
LL p;
LL fast(LL a,LL n)
{
LL ans=1;
while(n)
{
// printf("a=%lld n=%lld\n",a,n);
if(n&1) ans=ans*a%p;
a=a*a%p;
n>>=1;
}
//
return ans;
}
int main()
{
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
ma.clear();
LL n,x0,a,b;
scanf("%lld%lld%lld%lld%lld",&n,&x0,&a,&b,&p);
LL s=fast(a,m);
//int s=_pow(a,m);
// int s=1;
LL temp=(a*x0-x0+b+p)%p;
LL sum=temp;
for(int i=1;i<=x;i++){///预处理
sum=sum*s%p;
if(!(ma.count(sum)))
ma[sum]=i*m;
}
int q;
LL v;
scanf("%d",&q);
while(q--)
{
scanf("%lld",&v);
temp=(a*v-v+b+p)%p;
LL ans=p+1;
if(a==0){
if(v==x0) puts("0");
else if(v==b) puts("1");
else puts("-1");
}
else if(a==1){ ///xn=x0+n*b
if(v==x0) puts("0");
else if((v-x0)>=0&&(v-x0)%b==0&&(v-x0)/b<n)
printf("%lld\n",(v-x0)/b);
else if((v-x0+p)%b==0&&(v-x0+p)/b<n)
printf("%lld\n",(v-x0+p)/b);
else puts("-1");
}
else{
sum=temp;
for(int i=0;i<=y;i++){
if(ma.count(sum)){
ans=min(ans,ma[sum]-(LL)i);
}
sum=(LL)sum*a%p;
}
if(ans<=p&&ans<n) printf("%lld\n",ans);
else puts("-1");
}
}
}
return 0;
}