generator 2

题目链接
牛客多校 第五场
题目大意就是给出 x0,a,b,p,n, 求出一个数v在x的数列中最先出现的下标是多少
其中 x i = a ∗ x i − 1 + b x_{i} = a*x_{i - 1} + b xi=axi1+b
通过题目中给的式子 我们可以化简得到:
x n = x 0 ∗ a n + b ∗ ( a n − 1 − 1 ) ( a − 1 ) x_{n} = x_{0}*a^{n} + b*\frac{(a^{n - 1} - 1)}{(a - 1)} xn=x0an+b(a1)(an11)
将v带入化简我们可以得到
a n = v ∗ ( a − 1 ) + 1 x 0 ∗ ( a − 1 ) + b a^{n} = \frac{v*(a - 1) + 1}{x0*(a - 1) + b} an=x0(a1)+bv(a1)+1
根据BSGS 公式为 a n ≡ b ( m o d m ) a^{n}≡b\pmod{m} anb(modm)
我们可将其化简为 ( a t ) i ≡ b ∗ a j ( m o d m ) (a^{t} )^{i}≡b*a^{j}\pmod{m} (at)ibaj(modm)
具体得证明 可以去网上查一查,将其带入本题那么 式子就可以化简为
( a t ) i = a j ∗ v ∗ ( a − 1 ) + 1 x 0 ∗ ( a − 1 ) + b ⟺ ( a t ) i ∗ x 0 ∗ ( a − 1 ) + b v ∗ ( a − 1 ) + 1 = a j (a^{t} )^{i}= a^{j} *\frac{v*(a - 1) + 1}{x0*(a - 1) + b}\Longleftrightarrow(a^{t} )^{i}*\frac{x0*(a - 1) + b}{v*(a - 1) + 1} = a^{j} (at)i=ajx0(a1)+bv(a1)+1(at)iv(a1)+1x0(a1)+b=aj
带入我们枚举将其存起来,然后在枚举i一一查询找到返回即可,注意我们不能向平常一样t取 p \sqrt{p} p
这里应该取 p ∗ 2 3 p*\frac{2}{3} p32 即j的上界,i从0到 p ∗ 1 3 p*\frac{1}{3} p31
代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,x0,a,b,p;
unordered_map<int ,int> mp;
int power(int a, int b,int mod){
    int ans = 1;
    while(b){
        if(b&1){
            ans = a*ans %mod;
        }
        b >>= 1;
        a = a*a%mod;
    }
    return ans%mod;
}
int flag;
int t,t1;
void pre_BSGS(int a, int p){
    mp.clear();
    a %= p;
    t = pow(p, 1.0/3.0) + 1;
    t1 = pow(p, 2.0/3.0) + 1;
    int val = 1;
    for(int j = 0; j <= t1; j ++){
        if(j == t1) flag = val;
        mp[val] = j;
        val = val*a%p;
    }
}

int BSGS(int a, int b, int p){
    for(int i = 1; i <= t; i ++){
        b = b*flag%p;
        if(mp.count(b))
        {
            return i*t1 - mp[b];
        }
    }
    return -1;

}
signed main()
{
    int t;
    scanf("%lld",&t);
    while(t --){
        scanf("%lld%lld%lld%lld%lld",&n,&x0,&a,&b,&p);
        pre_BSGS(a, p);
        int q;
        scanf("%lld",&q);
        while(q --){
            int v;
            scanf("%lld",&v);
            if(a == 1){
                if(v == x0){
                    printf("0\n");
                }
                else{
                    int temp = (v - x0 + p) % p;
                    if(temp/b < n){
                        printf("%lld\n",temp/b);
                    }
                    else puts("-1");
                }
            }
            else if(a == 0){
                if(v == x0){
                    printf("0\n");
                }
                else if(b == v&&n >= 1){
                    printf("1\n");
                }
                else printf("-1\n");
            }
            else{
                int temp = power(((v%p*(a - 1 + p)%p)%p+ b)%p,p-2,p) *((x0%p*(a - 1 + p)%p)%p + b)%p;
                int ans =  BSGS(a, temp, p);
                if(ans < n) printf("%lld\n",ans);
                else printf("-1\n");
            }
        }

    }
}
/*

45
1000000000000000000 1 5 0 1000000007
5
1
10
1000000006
12345678
1234567*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值