C-generator 2 (BSGS)2019牛客第五场

题目链接:

题意:给出 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=(axi1+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=anx0+an1b+an2b+...+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=anx+1ab(1an)
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(ax0x0+b)(avv+b)modp

此时看到这个式子,我们就想到了BSGS
求解关于x的方程: y x ≡ z m o d p y^x\equiv z modp yxzmodp
因为这里的 g c d ( y , p ) = 1 gcd(y,p)=1 gcd(y,p)=1

我们把 x x x写成一个 a m − b am−b amb的形式
那么,原式变成了
y a m ≡ z y b ( m o d p ) y^{am}\equiv zy^b(mod p) yamzyb(modp)

我们求出所有b可能的取值 ( 0   m − 1 ) (0~m-1) (0 m1),并且计算右边的值
同时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(ax0x0+b)ay(avv+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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值