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

来源:互联网 发布:java springmvc 分页 编辑:程序博客网 时间:2024/06/05 02:52

题目描述

传送门

题解

先说一下暴力的写法,这道题最好是写一个小暴力拍一下:
按照题目描述的枚举即可,但是要注意判断无解的条件。通过观察可以发现这个数列是存在循环节的,如果已经找到了循环节并且第一个循环节内没有满足条件的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 longLL 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为质数,不要搞错了。
④其实这道题是在式子的两边同时加上了一个常数,然后化成通项公式。也就是找出X1Xn的关系。这样的话要保证X1Xn的系数相等(把a提出),且可以一直推下去(也就是说保证式子左边的一坨和右边除去系数的一坨相同,则同时加上的常数可以计算出来)。

0 0
原创粉丝点击