题解——随机数生成器

[SDOI2013] 随机数生成器

题目背景

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

题目描述

最近小 W 准备读一本新书,这本书一共有 p 页,页码范围为 0p1

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

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

xi+1a×xi+b(modp)

其中 mod 表示取余操作。

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

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

输入格式

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

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

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

输出格式

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

样例 #1

样例输入 #1

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

样例输出 #1

1 
3 
-1

提示

数据规模与约定

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

  • 1T50
  • 0a,b,x1,t<p2p109
  • p 为质数。

题解

首先大致拆开一下式子

x1=x1

x2=ax2+b

x3=a2x2+ab+b

越拆越觉得涉及到高次同余式,考虑BSGS,这启发我们将式子化为高次式

考虑转化为等比数列,设xn+1+m=a(xn+m)=axn+b+m

(a1)m=b
变形,得到m=ba1,所以设Sn=xn+ba1

有:Sn=aSn1

进一步的t+ba1=Sn=an1(x1+ba1),除过去有

t+ba1x1+ba1an1(modp)

所以说,我们解出这个同余式的解即可

不过有几个特殊情况需要判断,就是a=1/0b=1/0等的情况

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
#define int long long
map<int, int>h;
int t, a, b, x, p, ans;
int power(int a, int b, int p) {
	int ans = 1;
	while (b) {
		if (b & 1)ans = ans * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return ans % p;
}
int BSGS(int x) {
	h.clear();
	int a_inv = power(a - 1, p - 2, p);
	int x_a_inv = power((x + b * a_inv % p) % p, p - 2, p);
	int s = (t + b * a_inv) % p * x_a_inv % p;
	int pq = sqrt(p) + 1;
	for (int i = 0; i < pq; i++) {
		h[s * power(a, i, p) % p] = i;
	}
	a = power(a, pq, p);
	if (!a) {
		return s == 0 ? 1 : -1;
	}
	for (int i = 1; i <= pq; i++) {
		int sq = power(a, i, p);
		if (h.find(sq) != h.end() && pq * i - h[sq] > 0) {
			return pq * i - h[sq] + 1;
		}
	}
	return -1;
}
signed main() {
	int T;
	scanf("%lld", &T);
	while (T--) {
		scanf("%lld%lld%lld%lld%lld", &p, &a, &b, &x, &t);
		ans = 0;
		if (x == t) {
			puts("1");
		}
		else if (a == 0) {
			if (t == b)puts("2");
			else puts("-1");
		}
		else if (a == 1 && b == 0) {
			puts("-1");
		}
		else if (a == 1) {
			int inv = power(b, p - 2, p);
			ans = ((t - x) % p + p) % p * inv % p;
			printf("%lld\n", ans + 1);
		}
		else {
			printf("%lld\n", BSGS(x));
		}
	}
}

如何想到这个做法?由递归式会使得式子变成高次式,就启发BSGS,BSGS又能启发我们转化等比数列

posted @   spdarkle  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示