[bzoj 3122] [Sdoi2013]随机数生成器:数论,同余,分类讨论,BSGS

题意:给四个参数p, a, b, x1,生成一个数列,以x1为首项,x[i+1] = (ax[i]+b) mod p (i>=1),问是否存在x[i]=t,如果存在,求出最小的i。0<=a, b, x1, t < p,2<=p<=10^9,p为素数。

以前看过这道题,知道思路。然而AC还是不太容易啊……

如果这是数学试卷上的一道数列大题,如果递推式不mod p,作为高中生的我们会求一求通项,解一个方程。

现在,这是一道OI题,每次mod p——但这并不妨碍我们求通项,因为同余号和等号具有许多一致之处。

求一求特征根,先不急着做除法,得到

(a1)xb(modp)

即便p是素数,也要考察 a10 的情形。在模算术中,0没有逆元,正如0不能作分母。

a=1 ,原递推式为

xi+1=(xi+b)modp

容易写出通项

xn=x1+(n1)bmodp

a1 ,原递推式化为

xi+1+ba1a(xi+ba1)(modp)

通项公式

xn=(an1(x1+ba1)ba1)modp

通项求完了,让我们来解方程。


ai=t

i 是满足此方程的最小正整数。

a=1

(i1)btx1

b=0 {an} 是常数数列。若 x1=t i=1 ;否则,无解。
b0 i(tx1b+1) ,注意 i 是正整数。

a=0
数列首项为 x1 ,其余项为 b 。若x1=t i=1 ;否则,若 t=b i=2 ;否则,无解。

a1 a0

ai1(x1+ba1)ba1+t

x1+ba10 ,返回通项公式看一看,发现 {xn} 是常数数列。若 x1=t i=1 ;否则,无解。也可以直接看同余式右边是否为 0 ,这两个条件是等价的。
x1+ba1≢0,则 ai+1ba1+tx1+ba1 。离散对数问题。我们用大步小步算法求出此方程的最小正整数解,或者说明无解。

关于大步小步算法,请听下回分解~

综上所述,我们要考察a是否为1或0。若a=1,考察b是否为0。若a!=1且a!=0,考察(x1+b*inv(a-1))%p是否为0。

不单独考察a=0也可以,这要求在BSGS过程中特判a^0=1。但是0^0感觉有点怪怪的……

#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
ll p;

inline ll fast_exp(ll x, ll n)
{
    ll z = 1;
    for (ll y = x; n; n >>= 1, y = y*y%p)
        if (n & 1)
            z = z*y%p;
    return z;   
}

inline ll inv(ll x)
{
    return fast_exp(x, p-2);
}

ll bsgs(ll a, ll y) // minimum x such that a^x mod p = y
{
    ll m = sqrt(p);
    map<ll, ll> M;
    for (ll i = 1, t = a*y%p; i <= m; ++i, t = t*a%p)
        M[t] = i;
    for (ll i = m, b = fast_exp(a, m), t = b; i-m < p-1; i += m, t = t*b % p)
        if (M.count(t))
            return i-M[t];
    return -2;
}

int main()
{
    int T;
    ll a, b, x1, t;
    for (scanf("%d", &T); T--; ) {
        scanf("%lld %lld %lld %lld %lld", &p, &a, &b, &x1, &t);
        ll ans = -1;
        if (a == 0)
            ans = x1 == t ? 1 : (b == t ? 2 : -1);
        else if (a == 1) {
            if (b) {
                ans = ((t-x1+p)%p*inv(b)+1)%p;
                ans = ans ? ans : p;                
            } else
                ans = x1 == t ? 1 : -1;
        } else {
            ll c = b*inv(a-1)%p, d = (x1+c)%p;
            if (d)
                ans = bsgs(a, (t+c)*inv((x1+c)%p)%p) + 1;
            else
                ans = (t+c)%p ? -1 : 1;
        }
        printf("%lld\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值