题意:
给你一个长度为n的序列求法:
x
i
=
a
∗
x
i
−
1
+
b
x_i=a*x_{i-1}+b
xi=a∗xi−1+b
现在问你
x
i
%
p
=
b
x_i\%p=b
xi%p=b的最小的i是多少。
i
<
=
1
e
18
&
&
a
,
b
,
p
<
=
1
e
9
&
&
p
i
s
a
p
r
i
m
e
i<=1e18\&\&a,b,p<=1e9\&\&p\ is\ a\ prime
i<=1e18&&a,b,p<=1e9&&p is a prime
题解:
推一下即可发现
x
i
=
a
i
∗
x
0
+
a
i
−
1
∗
b
+
a
i
−
2
∗
b
+
.
.
.
+
b
xi=a^i*x_0+a^{i-1}*b+a^{i-2}*b+...+b
xi=ai∗x0+ai−1∗b+ai−2∗b+...+b
那么后半部分就是一个等比序列求和,那么式子就变成
b
(
a
n
−
1
−
1
)
a
−
1
+
x
0
∗
a
n
=
x
i
\frac{b(a^{n-1}-1)}{a-1}+x_0*a^n=xi
a−1b(an−1−1)+x0∗an=xi
BSGS算法大致就是将
a
x
%
p
=
b
%
p
a^x\%p=b\%p
ax%p=b%p根据同余的特性转化成
a
i
∗
x
+
j
%
p
=
b
%
p
a^{i*x+j}\%p=b\%p
ai∗x+j%p=b%p,在转化成
a
i
∗
x
%
p
=
b
∗
a
−
j
%
p
a^{i*x}\%p=b*a^{-j}\%p
ai∗x%p=b∗a−j%p这样的话我们确定x,就可以枚举i来存下所有的
a
i
∗
x
a^{i*x}
ai∗x,之后再枚举所有的
b
∗
a
−
j
%
p
b*a^{-j}\%p
b∗a−j%p,看看是否有值与之相等的
a
i
∗
x
a^{i*x}
ai∗x出现过。
这道题由于它查询有1000*40个,所以大致是将x确定为1e6,i和j从0枚举到1000,但是T了,我调到x=5e5,i,j=2e3即可过
但是有别的办法或者优化来着,我看好多人不到1000ms
#include<bits/stdc++.h>
using namespace std;
#define ll long long
unordered_map<ll,ll>vis;
ll n,x0,a,b,mod;
ll qpow(ll aa,ll bb)
{
ll ret=aa,ans=1;
while(bb)
{
if(bb&1)
ans=ans*ret%mod;
ret=ret*ret%mod;
bb>>=1;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld%lld%lld%lld",&n,&x0,&a,&b,&mod);
vis.clear();
ll ret=qpow(a,2000),s=1;
for(ll j=0;j<=500000;j++)
{
if(!vis.count(s))
vis[s]=j*2000ll;
s=s*ret%mod;
}
ll den=qpow((b+x0*(a-1))%mod,mod-2);
int q;
scanf("%d",&q);
while(q--)
{
ll v;
scanf("%lld",&v);
if(a==0)
{
if(v==x0)
printf("0\n");
else if(v==b)
printf("1\n");
else
printf("-1\n");
continue;
}
if(a==1)
{
ll ans=(v-x0+mod)*qpow(b,mod-2)%mod;
if(ans>=n)
printf("-1\n");
else
printf("%lld\n",ans);
continue;
}
ll ans=mod+1;
ret=((v*(a-1)+b)%mod)*den%mod;
ll inva=qpow(a,mod-2);
for(int i=0;i<2000;i++)
{
if(vis.count(ret))
ans=min(ans,vis[ret]+i);
ret=ret*inva%mod;
}
if(ans==mod+1||ans>=n)
printf("-1\n");
else
printf("%lld\n",ans);
}
}
return 0;
}