【Coel.解题报告】【2021的最后一道紫题】[SDOI2013] 随机数生成器
题前碎语
再见了,2021!
回顾这一年,我的\(OI\)力有了不少长进(虽然只是在下半年),也认识了一些新朋友,知道了更多新知识。
尽管有很多不舍,但还是要向这充实的一年做个道别。
那就用这道紫题,作为今年的收尾吧!
题目简介
题目背景
小 W 喜欢读书,尤其喜欢读《约翰克里斯朵夫》。
题目描述
最近小 W 准备读一本新书,这本书一共有 \(p\) 页,页码范围为 \(0∼p−1\) 。
小 W 很忙,所以每天只能读一页书。为了使事情有趣一些,他打算使用 NOI2012 上学习的线性同余法生成一个序列,来决定每天具体读哪一页。
我们用 \(x_i\) 来表示通过这种方法生成出来的第 \(i\) 个数,也即小 W 第 \(i\) 天会读哪一页。这个方法需要设置 \(3\) 个参数 \(a,b,x_1\),满足 \(0\leq a,b,x_1\lt p\),且 \(a,b,x_1\)都是整数。按照下面的公式生成出来一系列的整数:
其中\(mod\)表示取余操作。
但是这种方法可能导致某两天读的页码一样。
小 W 要读这本书的第 \(t\) 页,所以他想知道最早在哪一天能读到第 \(t\) 页,或者指出他永远不会读到第 \(t\) 页。
输入格式
本题单测试点内有多组测试数据。
第一行是一个整数 \(T\),表示测试数据组数。
接下来T行,每行有五个整数\(p, a, b, x_1, t\),表示一组数据。
输出格式
对于每组数据,输出一行一个整数表示他最早读到第 \(t\) 页是哪一天。如果他永远不会读到第 \(t\) 页,输出\(-1\)。
利用等比数列进行递推。
题意给的式子并不好推,不过可以先化作等比数列:
然后?
做个移项!
因为 \(a,b,x_1,x_n\)(也就是 \(t\)) 都是已知的,我们只需要把 \(n-1\) 给求出来就好了。
求高次同余方程用什么?\(BSGS\)!
所以就是一个套柿子的过程。
但是要注意,如果 \(a=1\) 或者 \(a=0\) ,这个柿子就不成立了,不过可以直接特判解决。
代码如下:
#include <cstdio>
#include <cctype>
#include <cmath>
#include <map>
typedef long long ll;
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if(ch == '-') f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline ll qpow(ll a, ll b, ll p) {
ll ans = 1;
while (b) {
if(b & 1) ans = ans * a % p;
a = a * a % p;
b >>= 1;
}
return ans;
}
inline ll Exgcd(ll a, ll b, ll &x, ll &y) {
if(b == 0) {
x = 1, y = 0;
return a;
}
ll r = Exgcd(b, a % b, x, y), temp = x;
x = y, y = temp - a / b * y;
return r;
}
inline ll inv(ll a, ll p) {
//扩欧求逆元(就是不用费马,哈哈)
ll x, y;
Exgcd(a, p, x, y);
return (x % p + p) % p;
}
inline ll bsgs(ll a, ll b, ll p) {
if(p % a == 0)return -1;
std::map<ll, ll> Hash;
ll m = (ll)(sqrt(p)), mul = b % p, temp = 1;
Hash[mul] = 1;
for (int i = 1; i <= m; i ++) {
mul = mul * a % p;
Hash[mul] = i + 1;
}
mul = qpow(a, m, p);
for (int i = 1; i <= m; i++) {
temp = temp * mul % p;
if(Hash.find(temp) != Hash.end())
return ((i * m - Hash[temp] + 1) % p + p) % p + 1;
}
return -1;
}
int main() {
ll T = read();
while (T--) {
ll p = read(), a = read(), b = read(), x1 = read(), t = read();
if(x1 == t) puts("1");
else if(a == 1 && b == 0) puts("-1");
else if(a == 0) puts(t == b ? "2" : "-1");
else if(a == 1) printf("%lld\n",(((t - x1) % p + p) % p) * inv(b, p) % p + 1);
else {
ll in = inv(a - 1, p);
ll one = (t % p + b % p * in) % p, two = inv (x1 % p + b * in % p, p);//把两个括号的内容分别求出来,防止代码太长
printf("%lld\n", bsgs(a, one * two % p, p));
}
}
return 0;
}
题后闲话
那么,2021的最后一道紫题的题解,就这样结束了。
虽然有些不舍,不过还是要说一声再见。