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

题目背景

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

题目描述

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

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

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

\(x_{i+1} \equiv a \times 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\n");
            continue;
        }
        if(a==1){
            if(b==0){
                if(x1==t)printf("1\n");
                else printf("-1\n");
            }
            else{
                printf("%lld\n",(((t-x1)%p+p)%p*pow(b,p-2,p))%p+1);
            }
            continue;
        }
        if(a==0){
            if(x1==t){
                printf("1\n");
            }
            else{
                if(t==b){
                    printf("2\n");
                }
                else printf("-1\n");
            }
            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\n");
        else printf("%lld\n",(k+1));
    }
    return 0;
}
posted @ 2020-07-11 08:14  VagrantAC  阅读(163)  评论(0编辑  收藏  举报