2019牛客暑期多校5C:generator 2【BSGS】

题目:

2019牛客暑假多校5C:generator 2

题意:

给出N,X0,a,b,p,数列Xi = (aXi-1 + b)mod p,给出Q次询问,每次求V在数列中出现的最小位置是多少

分析:

容易推出Xn的通项公式:

X_n = \frac{X_0(a-1)+b}{a-1}a^n-\frac{b}{a-1}~~(mod~p)

题意要求得最小的n,使得Xn = v;代入上式得到:

a^n = \frac{v(a-1)+b}{X_0(a-1)+b}~~(mod~p)

右边的值已知,剩下的就是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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值