2526. 随机数生成器
题目链接
2526. 随机数生成器
小 \(W\) 喜欢读书,尤其喜欢读《约翰克里斯朵夫》。
最近小 \(W\) 准备读一本新书,这本书一共有 \(p\) 页,页码范围为 \(0..p-1\)。
小 \(W\) 很忙,所以每天只能读一页书。
为了使事情有趣一些,他打算使用 NOI2012 上学习的线性同余法生成一个序列,来决定每天具体读哪一页。
我们用 \(X_i\) 来表示通过这种方法生成出来的第 \(i\) 个数,也即小 \(W\) 第 \(i\) 天会读哪一页。
这个方法需要设置 \(3\) 个参数 \(a,b,X_1\),满足 \(0 \le a,b,X_1 \le p-1\),且 \(a,b,X_1\) 都是整数。
按照下面的公式生成出来一系列的整数。
\(X_{i+1}=(aX_i+b) \bmod p\)
其中 \(\bmod p\) 表示前面的数除以 \(p\) 的余数。
可以发现,这个序列中下一个数总是由上一个数生成的,而且每一项都在 \(0..p-1\) 这个范围内,是一个合法的页码。
同时需要注意,这种方法有可能导致某两天读的页码完全一样。
小 \(W\) 非常急切的想去读这本书的第 \(t\) 页。
所以他想知道,对于一组给定的 \(a,b,X_1\),如果使用线性同余法来生成每一天读的页码,最早读到第 \(t\) 页是在哪一天,或者指出他永远不会读到第 \(t\) 页。
输入格式
输入含有多组数据,第一行一个正整数 \(T\),表示这个测试点内的数据组数。
接下来 \(T\) 行,每行有五个整数 \(p,a,b,X_1,t\),表示一组数据。保证 \(X_1\) 和 \(t\) 都是合法的页码。
注意:\(P\) 一定为质数。
输出格式
共 \(T\) 行,每行一个整数表示他最早读到第 \(t\) 页是哪一天。
如果他永远不会读到第 \(t\) 页,输出 \(-1\)。
数据范围
\(0 \le a \le p-1\),
\(0 \le b \le p-1\),
\(2 \le p \le 10^9\)
输入样例:
3
7 1 1 3 3
7 2 2 2 0
7 2 2 2 1
输出样例:
1
3
-1
解题思路
bsgs
对于 \(X_{i+1}=(aX_i+b) \bmod p\),设 \(X_{i+1}+c=a(X_i+c)\),解得 \(c=b/(a-1)\),则有 \(X_{n}+b/(a-1)=a^{n-1}\times (X_1+b/(a-1))\),其中 \(X_n=t\),转换得 \(a^{n-1}\equiv \frac{t+b/(a-1)}{X_1+b/(a-1)}(\bmod p)\),即 bsgs 模板题,另外这里值得一提的是,\(X_1+b/(a-1)\) 可能是 \(p\) 的倍数,即不能用逆元处理,特判这部分后另外要求 \(a^{n-1}\bmod p\) 是一个整数,则 \(t+b/(a-1)\) 也必须要是 \(p\) 的倍数才可能有解,而 \(X_1,t\) 的范围都小于 \(p\),则上下只存在相等的情况,即 \(a^{n-1}=1\),则 \(n=1\)
- 时间复杂度:\(O(\sqrt{p})\)
代码
// Problem: 随机数生成器
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2528/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int T,p,a,b,x,t;
int ksm(int a,int b,int p)
{
int res=1%p;
while(b)
{
if(b&1)res=1ll*res*a%p;
a=1ll*a*a%p;
b>>=1;
}
return res;
}
int inv(int a,int p)
{
return ksm(a,p-2,p);
}
int bsgs(int a,int b,int p)
{
b%=p;
if(1==b)return 0;
unordered_map<int,int> hash;
int k=sqrt(p)+1;
for(int i=0,j=b;i<k;i++)
{
hash[j]=i;
j=(LL)j*a%p;
}
int ak=1;
for(int i=1;i<=k;i++)ak=(LL)ak*a%p;
for(int i=1,j=ak;i<=k;i++)
{
if(hash.count(j))return i*k-hash[j];
j=(LL)j*ak%p;
}
return -1;
}
int main()
{
for(cin>>T;T;T--)
{
cin>>p>>a>>b>>x>>t;
if(a==0)
{
if(x==t)puts("1");
else if(b==t)puts("2");
else
puts("-1");
}
else if(a==1)
{
if(b==0)puts(x==t?"1":"-1");
else
cout<<(LL)inv(b,p)*(t-x+p)%p+1<<'\n';
}
else
{
int A=(LL)b*inv(a-1,p)%p;
int B=(x+A)%p;
if(B==0)
puts((-A+p)%p==t?"1":"-1");
else
{
int C=(t+A)%p;
int res=bsgs(a,(LL)C*inv(B,p)%p,p);
if(res>=0)cout<<res+1<<'\n';
else
puts("-1");
}
}
}
return 0;
}