「Luogu3306」[SDOI2013]随机数生成器

本文详细解析了Luogu3306题目的解决方案,该题目涉及随机数生成器的数学原理及算法实现。通过转换递推公式并利用BSGS算法求解离散对数问题,文章提供了完整的C++代码示例。

「Luogu3306」[SDOI2013]随机数生成器

problem

Solution

我们来回忆一下数列递推公式求通项的方法

\(Y_i=X_i+\frac{b}{a-1}\),则有

\[Y_i=aY_{i-1}\Rightarrow Y_i=a^{i-1}Y_1\]

于是题目转化为求解方程

\[t+\frac b{a-1}\equiv a^{x-1}(X_1+\frac b{a-1})\pmod p\]

\[\Rightarrow a^{x-1}\equiv\frac{t+\frac b{a-1}}{X_1+\frac b{a-1}}\pmod p\]

好的,这个东西我们只要用BSGS就可以了

End?


几个细节:

\(X_1=t\)时,直接输出\(1\)

\(a=0\)时,输出若\(b=t\)则输出\(2\),否则无解,\(X_1=t\)的情况已经特判

\(a=1\)时,方程实际上为

\[(x-1)b\equiv t-X_1\pmod p\]

根据裴蜀定理判掉无解,然后直接算\(x-1\)即可

需要注意的是如果\(\frac{t-X_1} b=p\),则不需要取模直接输出

True End

Code

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <map>
#define mp(x,y) (make_pair((x),(y)))
#define inv(x) (fastpow((x),p-2))
using namespace std;
typedef long long ll;

template <typename T>void read(T &t)
{
    t=0;int f=0;char c=getchar();
    while(!isdigit(c)){f|=c=='-';c=getchar();}
    while(isdigit(c)){t=t*10+c-'0';c=getchar();}
    if(f)t=-t;
}

int T;

ll p,a,b,X1,t;
ll m;

ll fastpow(ll a,ll b)
{
    ll re=1,base=a;
    while(b)
    {
        if(b&1)
            re=re*base%p;
        base=base*base%p;
        b>>=1;
    }
    return re;
}

map<ll,ll> rec;
void Pre()
{
    rec.clear();
    ll tmp=1;
    for(register int i=0;i<m;++i,tmp=tmp*a%p)
        rec.insert(mp(tmp,i));
}

ll BSGS()
{
    ll iatm=inv(fastpow(a,m)),tmp=1;
    for(register int i=0;i<m;++i,tmp=tmp*iatm%p)
        if(rec.find(b*tmp%p)!=rec.end())
            return i*m+rec[b*tmp%p];
    return -2;
}

ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}

int main()
{
    read(T);
    while(T--)
    {
        read(p),read(a),read(b),read(X1),read(t);
        if(X1==t)
        {
            puts("1");
            continue;
        }
        if(a==1)
        {
            t=(t-X1+p)%p;
            if(t%gcd(b,p))
                puts("-1");
            else
                printf("%lld\n",t*inv(b)+1==p?p:(t*inv(b)+1)%p);
            continue;
        }
        if(a==0)
        {
            printf("%lld\n",b==t?2:-1ll);
            continue;
        }
        b=b*inv(a-1)%p;
        b=(t+b)*inv(X1+b)%p;
        m=ceil(sqrt(p));
        Pre();
        printf("%lld\n",BSGS()+1);
    }
    return 0;
}

转载于:https://www.cnblogs.com/lizbaka/p/10656151.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值