【洛谷】P3306 [SDOI2013]---- 随机数生成器

题目背景

(W) 喜欢读书,尤其喜欢读《约翰克里斯朵夫》。

题目描述

最近小 (W) 准备读一本新书,这本书一共有 (p) 页,页码范围为 (0 sim p-1)

(W) 很忙,所以每天只能读一页书。为了使事情有趣一些,他打算使用 (NOI2012) 上学习的线性同余法生成一个序列,来决定每天具体读哪一页。

我们用 (x_i) 来表示通过这种方法生成出来的第 ii 个数,也即小 W 第 ii 天会读哪一页。这个方法需要设置 (3) 个参数 (a,b,x_1) ,满足 (0leq a,b,x_1lt p0),且 (a,b,x_1) 都是整数。按照下面的公式生成出来一系列的整数:

(x_{i+1} equiv a imes x_i+b pmod p)

其中 (mod) 表示取余操作。

但是这种方法可能导致某两天读的页码一样。

(W) 要读这本书的第 (t) 页,所以他想知道最早在哪一天能读到第 (t) 页,或者指出他永远不会读到第 (t) 页。

输入格式

本题单测试点内有多组测试数据

第一行是一个整数 (T) ,表示测试数据组数。

接下来T行,每行有五个整数 (p, a, b, x_1, t) ,表示一组数据。

输出格式

对于每组数据,输出一行一个整数表示他最早读到第 (t) 页是哪一天。如果他永远不会读到第 (t) 页,输出 (-1)

输入输出样例

输入 #1

3
7 1 1 3 3
7 2 2 2 0
7 2 2 2 1

输出 #1

1 
3 
-1

说明/提示

数据规模与约定

对于全部的测试点,保证:

  • (1 leq T leq 50)
  • (0 leq a, b, x_1, t lt p,2 leq p leq 10^9)
  • (p) 为质数。

题解

已知 (a,b,x1,t,p) ,先列出前几项的公式

[x1 = x_1 \ x2 = ax_1+b\ x3 = a^2x_1+ab+b \ x4 = a^3x_1+a^2b + ab + b\ \ x_n = a^{n-1}x_1 + b(1+a+...+a^{n-1}) \ x_n = a^{n-1}x_1 + b(frac{a^{n-1}-1}{a-1}) \ a^{n-1} = frac{(a-1)t+b}{b+x_1(a-1)} ]

因为 (p) 是质数,之后使用 (BSGS) 算法进行求解。

上式中,(a) 不能等于 (1) ,对于 (a=1) ,存在

[x1 = x_1 \ x2 = x_1+b\ x3 = x_1+2b \ x4 = x_1+3b\ \ x_n = x_1+(n-1)b \ b=frac{x_n-x_1}{n-1} ]

注意

  • 需要进行 (a = 0) 的所有特殊状态
  • 如果 (x_1=t) 直接输出 (1)

代码

#include <map>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
inline ll multi(ll a,ll b,ll mod){
    ll ret=0;
    if(b<0){
        a=-a;b=-b;
    }
    while(b){
        if(b&1)ret=(ret+a)%mod;
        b>>=1;
        a=(a+a)%mod;
    }
    return (ret%mod+mod)%mod;
}
inline ll pow(ll a,ll b,ll mod){
    ll ret=1;
    while(b){
        if(b&1)ret=multi(ret,a,mod);
        b>>=1;
        a=multi(a,a,mod);
    }
    return (ret%mod+mod)%mod;
}
inline ll BSGS(ll a,ll b,ll m){
    ll k=ceil(sqrt(m))+1;
    map<ll,ll>M;
    M.clear();
    for(int i=0;i<=k;++i){
        M[b]=i;
        b=multi(b,a,m);
    }
    a=pow(a,k,m);
    ll ret=a;
    for(int i=1;i<=k;++i){
        if(M.count(ret)){
            return i*k-M[ret];
        }
        ret=multi(ret,a,m);
    }
    return -1;// 无解
}
int main(){
    int _;
    scanf("%d",&_);
    while(_--){
        ll p,a,b,x1,t;
        scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&x1,&t);
        if(x1==t){
            printf("1
");
            continue;
        }
        if(a==1){
            if(b==0){
                if(x1==t)printf("1
");
                else printf("-1
");
            }
            else{
                printf("%lld
",(((t-x1)%p+p)%p*pow(b,p-2,p))%p+1);
            }
            continue;
        }
        if(a==0){
            if(x1==t){
                printf("1
");
            }
            else{
                if(t==b){
                    printf("2
");
                }
                else printf("-1
");
            }
            continue;
        }
        ll aa=a%p;
        ll ss=pow(((a-1)*x1+b)%p,p-2,p);
        ll bb=((((a-1)*t+b)%p*ss)%p+p)%p;
        ll pp=p;
        ll k=BSGS(aa,bb,pp);
        if(k==-1)printf("-1
");
        else printf("%lld
",(k+1));
    }
    return 0;
}
新赛季的开始
原文地址:https://www.cnblogs.com/VagrantAC/p/13282338.html