题目:
题意:
给出N,X0,a,b,p,数列Xi = (aXi-1 + b)mod p,给出Q次询问,每次求V在数列中出现的最小位置是多少
分析:
容易推出Xn的通项公式:
题意要求得最小的n,使得Xn = v;代入上式得到:
右边的值已知,剩下的就是BSGS的模板了,题目给出了Q次询问,普通的分sqrt(p)的做法是要TLE的;假设答案n = Ax-B,右边的一堆值 = C;那么有:a^(Ax) = C*a^B (mod p),A 属于[1,p/x],B属于[0,x),所以就可以预处理出左边a^(Ax)的值存起来,然后每次询问时,枚举右边的B,判断C*a^B是否存在即可得到一组解Ax-B;x的取值就非常的关键了; x = pow(p,1/3)就是一个好的取值,这样预处理O(pow(p,2/3)),每次询问O(pow(p,1/3));很好地解释了为啥它也叫"大步小步 "
这题涉及到求逆元,而0不存在逆元,所以要特判a = 1 和 a = 0;
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,x0,a,b,q,v,mod,t1,t2;
struct Hash {
static const int MOD = 1999997;
static const int N = 1e6+106;
int head[MOD + 10], nx[N], top;
int hs[N], id[N];
void init() {
memset(head, -1, sizeof head);
top = 0;
}
void insert(int x, int y) {
int k = x % MOD;
hs[top] = x; id[top] = y; nx[top] = head[k]; head[k] = top++;
}
int find(int x) {
int k = x % MOD;
for (int i = head[k]; i != -1; i = nx[i]) {
if (hs[i] == x) {
return id[i];
}
}
return -1;
}
}mp;
LL qpow(LL a,LL x,LL MOD){
LL res = 1;
while(x){
if(x&1) res = res*a%MOD;
a = a*a%MOD;
x >>= 1;
}
return res;
}
void init(){
mp.init();
t1 = ceil(pow(mod*1.0,1.0/3));
t2 = ceil(pow(mod*1.0,2.0/3));
LL k = qpow(a,t1,mod),val = 1;
for(int i = 1;i <= t2; ++i){
val = val * k % mod;
if(mp.find(val)==-1) mp.insert(val,i);
}
}
LL solve1(){
LL ans = (v-x0+mod)%mod*qpow(b,mod-2,mod)%mod;
return ans < n ? ans:-1;
}
LL solve2(){
v = (v*(a-1)+b)%mod*qpow((x0*(a-1)+b)%mod,mod-2,mod)%mod;
LL ans = 4e18;
for(int i = 0;i <= t1; ++i){
int tep = mp.find(v);
if(tep != -1) ans = min(ans,1ll*tep*t1-i);
v = v*a%mod;
}
return ans < n ? ans:-1;
}
int main(){
int Case; cin >> Case;
while(Case--){
cin >>n>>x0>>a>>b>>mod>>q;
if(a > 1) init();
while(q--){
cin >> v;
if(a == 0){
if(v == x0) puts("0");
else if(v==b && n>1) puts("1");
else puts("-1");
}
else if(a == 1){
if(b == 0){
if(x0 == v) puts("0");
else puts("-1");
}
else cout << solve1() << '\n';
}
else cout << solve2() << '\n';
}
}
return 0;
}