【BZOJ3122】【SDOI2013】随机数生成器(快速幂+BSGS)

题目描述

传送门

题解

先说一下暴力的写法,这道题最好是写一个小暴力拍一下:
按照题目描述的枚举即可,但是要注意判断无解的条件。通过观察可以发现这个数列是存在循环节的,如果已经找到了循环节并且第一个循环节内没有满足条件的 xn 那么就可以直接退出啦。
正解的话应该是把这个式子变一下形然后做BSGS,推倒如下:
原式可以化为:

Xi+1aXi+b(modp)

Xi+1+ba1aXi+b+ba1(modp)

Xi+1+ba1a(Xi+ba1)(modp)


Xn+ba1a(Xn1+ba1)(modp)

Xn1+ba1a(Xn2+ba1)(modp)

讲②代人①得到
Xn+ba1a2(Xn2+ba1)(modp)

很神奇啊对不对,迭代下去
于是我们可以得到:
Xn+ba1an1(X1+ba1)(modp)

我们发现,除了 an1 之外,其余各项都是常数,于是
an1(Xn+binv(a1))inv(X1+binv(a1))(modp)

其中inv为某数的逆元。
用BSGS算法求n-1即可
需要注意的一些小问题:
①在同余式中除以一个数等于乘以这个数的逆元。在模p意义下a的逆元为 ap2 ,这个可以通过费马小定理证明。
②因为满足0<=a,b,x1<=p-1,且p为质数,那么一定满足a-1和p互质,那么我们就可以用快速幂求a-1的逆元啦。
③一些特殊情况需要特判

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
#define LL long long

LL T,P,a,b,x1,t;
LL ny1,ny2,val1,val2,val3;
LL ans;
map <LL,LL> hash;

inline LL fast_pow(LL a,LL p){
    LL ans=1;
    for (;p;p>>=1,a=a*a%P)
      if (p&1)
        ans=ans*a%P;
    return ans;
}

inline LL BSGS(LL a,LL b){
    LL m=ceil(sqrt(P));
    LL a_m=fast_pow(a,m);
    hash.clear();

    LL mul=1;
    LL val=mul*b%P;
    hash[val]=0;
    for (LL j=1;j<=m;++j){
        mul=mul*a%P;
        val=mul*b%P;
        hash[val]=j;
    }

    mul=1;
    for (LL i=1;i<=m;++i){
        mul=mul*a_m%P;
        if (hash[mul]){
            LL x=i*m-hash[mul];
            return x+1;
        }
    }
    return -1;
}

int main(){
    scanf("%lld",&T);
    while (T--){
        scanf("%lld%lld%lld%lld%lld",&P,&a,&b,&x1,&t);

        //一坨特判
        if (t==x1){
            printf("1\n");
            continue;
        }
        if (a==0){
            if (t==b) printf("2\n");
            else printf("-1\n");
            continue;
        }
        if (a==1&&b==0){
            printf("-1\n");
            continue;
        }
        if (a==1){
            int nyb=fast_pow(b,P-2);
            ans=((((t-x1)%P+P)%P)*nyb%P)%P;
            printf("%lld\n",ans+1);
            continue;
        }


        ny1=fast_pow(a-1,P-2);
        val1=b*ny1%P;

        val2=(x1%P+val1)%P;
        ny2=fast_pow(val2,P-2);

        val3=(t+val1)%P;
        b=(val3*ny2)%P;

        ans=BSGS(a,b);
        printf("%lld\n",ans);
    }
}

总结

①注意负数的情况。
②先%后+还是先+后%想清楚。在纸上列列公式,实在不行先换元。
③在%p意义下a的逆元为 ap2 ,ap互质且p为质数,不要搞错了。
④其实这道题是在式子的两边同时加上了一个常数,然后化成通项公式。也就是找出 X1 Xn 的关系。这样的话要保证 X1 Xn 的系数相等(把a提出),且可以一直推下去(也就是说保证式子左边的一坨和右边除去系数的一坨相同,则同时加上的常数可以计算出来)。

你可能感兴趣的:(数论,快速幂,bzoj,BSGS,SDOI)