[SDOI2013] 随机数生成器

最近小 W 准备读一本新书,这本书一共有\(p\)页,页码范围为\(0 \sim 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\)都是整数。按照下面的公式生成出来一系列的整数:

\[x_{i+1} \equiv a \times x_i+b \pmod p \]

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

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

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


\(m\)天第一次读到\(t\)页,就有\(a^mx_1+b\frac{a^m-1}{a-1}\equiv t\pmod{p}\),后面一项是等比数列求和。

化下式子就是:

\[a^m\equiv (t+\frac{b}{a-1})\times (x_1+\frac{b}{a-1})^{-1}\pmod{p} \]

\(p\)又是质数,这就是个\(BSGS\)的板子式子了。注意这里认为的\(a\)\(>1\)\(a\leq 1\)的情况讨论一下就可以了。复杂度\(\mathcal{O}(T\sqrt{p}\log p)\)

#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
#define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef cn int cint;
typedef pair<int, int> pr;
il void rd(int &x){
    x = 0;
	rg int f(1); rg char c(gc);
	while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
	while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
	x*=f;
}
cint maxn = 34010;
int T, p, a, b, x, t, inv, sqr, pw[maxn] = {1}, pw2, nw, nw2, pos, ans;
pr num[maxn];
il int fpow(int a, int b, int ans = 1){
	for(; b; b >>= 1, a = 1ll*a*a%p)if(b&1)ans = 1ll*ans*a%p;
	return ans;
}
int main(){
	rd(T);
	while(T--){
		rd(p), rd(a), rd(b), rd(x), rd(t), inv = fpow(a-1, p-2);
		if(x == t)puts("1");
		else if(!a){
			if(x == t)puts("1");
			else if(b == t)puts("2");
			else puts("-1");
		}else if(a == 1){
			if(b)printf("%lld\n", 1ll*(t-x+p)*fpow(b, p-2)%p+1);
			else printf("%d\n", x == t ? 1 : -1);
		}
		else{
			t = (t+1ll*b*inv)%p*fpow(x+1ll*b*inv%p, p-2)%p, sqr = ceil(sqrt(p)), ans = -1;
			fp(i, 1, sqr)pw[i] = 1ll*pw[i-1]*a%p, num[i] = mp(pw[i], i);
			sort(num+1, num+1+sqr), pw2 = fpow(a, sqr), nw = 1;
			fp(i, 0, p/sqr){
				nw2 = 1ll*fpow(nw, p-2)*t%p;
				pos = lower_bound(num+1, num+1+sqr, mp(nw2, 0))->se;
				if(pw[pos] == nw2){ans = i*sqr+pos+1; break;}
				nw = 1ll*nw*pw2%p;
			}
			printf("%d\n", ans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值