bzoj3122 [Sdoi2013]随机数生成器(bsgs+扩欧+数列)

来源:互联网 发布:百度怎么推广淘宝店铺 编辑:程序博客网 时间:2024/05/17 04:53

Description
这里写图片描述

Input

输入含有多组数据,第一行一个正整数T,表示这个测试点内的数据组数。

接下来T行,每行有五个整数p,a,b,X1,t,表示一组数据。保证X1和t都是合法的页码。

注意:P一定为质数

Output

共T行,每行一个整数表示他最早读到第t页是哪一天。如果他永远不会读到第t页,输出-1。

Sample Input
3
7 1 1 3 3
7 2 2 2 0
7 2 2 2 1

Sample Output
1
3
-1

HINT
0<=a<=P-1,0<=b<=P-1,2<=P<=10^9

分析:
刚刚学了数列,看到这道题就不那么难受了

X[i+1]=(aX[i]+b)mod p
X[i+1]+u=a(X[i]+u)
(a-1)u=b
u=b/(a-1)
X[i+1]+u=(X[1]+u)*a^i
a^i=(X[i+1]+u)/(X[1]+u) (mod p)
已知X[i+1]
实际上就是
x^i=z (mod p),求解最小i

最后答案就是i+1

然而这只适用于a!=1的情况

当a==1的时候
这就是一个等差数列了
X[i+1]=X[1]+b*i
X[i+1]-X[1]=b*i
设x=i,z=X[i+1]-X[1]
式子就可以化简成
b*i=z (mod p)
b*i+k*p=z
我们先用扩欧求解b*i+k*p=1
最后把答案*z+1就可以了

注意

如果z不是gcd(b,p)的倍数,那么无解

一开始我的算法是:
i=(X[i+1]-X[1])*inv(b)
但是这样秒WA,我觉得一概是无法判断无解导致的

注意:p一定为质数
这样求逆元就可以用费马小定理了

tip

注意特殊情况的特判
x1==t,ans=1
a==0&&b==t,ans=2
a==0&&b!=t,ans=-1

一直连WA,最后才发现是主程序的问题
一个输出后忘了回车!!!

这里写代码片#include<cstdio>#include<iostream>#include<cstring>#include<map>#include<cmath>#define ll long longusing namespace std;map<ll,int> mp;ll p,z,t,x1,a,b,x,y;ll gcd(ll a,ll b){    ll r=a%b;    while (r)    {        a=b;b=r;        r=a%b;    }    return b;}ll KSM(ll a,ll b,ll p){    a%=p;    ll t=1;    while (b)    {        if (b&1)           t=(t%p*a%p)%p;        b>>=1;        a=(a%p*a%p)%p;    }    return t%p;}int bsgs(ll x,ll z,ll p){    mp.clear();    x%=p; z%=p;    if (x==0&&z==0) return 2;    if (x==0) return -1;    ll m=(ll)ceil(sqrt((double)p)),now=1;    mp[1]=m+1;    for (int i=1;i<m;i++)    {        now=(now%p*x%p)%p;        if (!mp[now]) mp[now]=i;    }    ll inv=1,tmp=KSM(x,p-m-1,p);    for (int k=0;k<m;k++)    {        int i=mp[(z%p*inv%p)%p];        if (i)        {            if (i==m+1) i=0;            return k*m+i+1;        }        inv=(inv%p*tmp%p)%p;    }    return -1;}void exgcd(ll a,ll b){    if (b==0)    {        x=1; y=0;        return;    }    else    {        exgcd(b,a%b);        int tt=y;        y=x-(a/b)*y;        x=tt;    }}ll inv(ll a,ll p){    return KSM(a,p-2,p);}int main(){    int T;    scanf("%d",&T);    while (T--)    {        scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&x1,&t);        if (x1==t)        {            printf("1\n");continue;        }         else if (a==0)        {            if (b==t) printf("2\n");            else printf("-1\n");            continue;        }        else if (a==1)   //等差数列         {            z=((t-x1)%p+p)%p;            if (z%gcd(b,p)!=0)            {                printf("-1\n");                continue;            }            exgcd(b,p);            x=(x%p*z%p)%p;            printf("%lld\n",(x%p+p)%p+1);        }        else   //等比         {            ll u=(b%p*inv(a-1,p)%p)%p;   //u=b/(a-1)            t=(t%p+u%p)%p;              x1=(x1%p+u%p)%p;            z=(t%p*inv(x1,p)%p)%p;   //(X[i+1]+u)/(X[1]+u)            printf("%d\n",bsgs(a,z,p));        }    }    return 0;}
阅读全文
0 0