题解 LuoguP3306 [SDOI2013] 随机数生成器

发布时间 2023-08-11 10:06:46作者: Chen_Jinhui

题目链接:【LuoguP3306】

前置知识

OI-Wiki:快速幂扩展欧几里得算法(exgcd)Baby Step, Giant Step 算法

题意

很清楚,不说。

分析

\(t=x\)

答案很明显为 \(1\),即在第一天就可以读到。

\(t\neq x\)

\(a=0\)

观察一下规律:

\[x_1\equiv x_1\pmod{p} \]

\[x_2\equiv b\pmod{p} \]

规律十分显著:

\[x_i\equiv\begin{cases}x_1&i=1\\b&i>1\end{cases}\pmod{p} \]

可以直接特判。

\(a=1\)

观察一下规律:

\[x_1\equiv x_1\pmod{p} \]

\[x_2\equiv x_1+b\pmod{p} \]

\[x_3\equiv x_1+2b\pmod{p} \]

规律十分显著:

\[x_i\equiv x_1+(i-1)b\pmod{p} \]

\[(i-1)b\equiv x_i-x_1\pmod{p} \]

然后就是用扩展欧几里得解同余方程了。

\(a>1\)

观察一下规律:

\[x_1\equiv x_1\pmod{p} \]

\[x_2\equiv ax_1+b\pmod{p} \]

\[x_3\equiv a^2x_1+ab+b\pmod{p} \]

规律十分显著:

\[x_i\equiv a^{i-1}x_1+\sum\limits_{j=0}^{i-2}a^jb\pmod{p} \]

很明显是一个等比数列。

等比数列如何求解?

首先设 \(S=\sum\limits_{j=0}^{i-2}a^j\)

\[aS=\sum\limits_{j=1}^{i-1}a^j \]

\[aS-S=\sum\limits_{j=1}^{i-1}a^j-\sum\limits_{j=0}^{i-2}a^j \]

\[(a-1)S=a^{i-1}-1 \]

\[S=\dfrac{a^{i-1}-1}{a-1} \]

\[\sum\limits_{j=0}^{i-1}a^jb = b\sum\limits_{j=0}^{i-1}a^j= b\dfrac{a^{i-1}-1}{a-1}=\dfrac{b}{1-a}-\dfrac{b}{1-a}a^{i-1} \]

\[a^{i-1}\equiv\dfrac{x_i-\frac{b}{1-a}}{x_1-\frac{b}{1-a}}\pmod{p} \]

\[a^{i-1}\equiv\dfrac{(a-1)x_i+b}{(a-1)x_1+b}\pmod{p} \]

然后求逆元之后利用 BSGS 算法求解同余高次方程。

最后可以发现其实推完公式之后就是 「SDOI2011」计算器

代码

//the code is from chenjh
#include<cstdio>
#include<cmath>
#include<unordered_map>
typedef long long LL;
LL qpow(LL a,int b,const int p){//快速幂。
	int ret=1;
	for(;b;b>>=1,a=a*a%p)if(b&1)ret=ret*a%p;
	return ret%p;
}
int exgcd(const int a,const int b,int&x,int&y){//扩展欧几里得。
	if(!b) return x=1,y=0,a;
	int d=exgcd(b,a%b,x,y);
	int z=x;x=y,y=z-y*(a/b);
	return d;
}
LL BSGS(int a,LL b,int p){//大步小步算法。
	std::unordered_map<LL,LL> h;h.clear();//std::unordered_map 基于哈希,理论上更快,实际上也比红黑树实现的 std::map 快。
	b%=p;
	int t=(LL)sqrt(p)+1;
	for(int j=0;j<t;j++) h[b*qpow(a,j,p)%p]=j;
	a=qpow(a,t,p);
	if(!a) return b?-1:1;
	for(int i=0;i<=t;i++){
		int v=qpow(a,i,p);
		LL j=h.find(v)==h.end()?-1:h[v];
		if(j>=0 && (LL)i*t-j>=0) return (LL)i*t-j;
	}
	return -1;
}
int main(){
	int T;scanf("%d",&T);
	for(int p,a,b,x,t;T--;){
		scanf("%d%d%d%d%d",&p,&a,&b,&x,&t);
		if(x==t) puts("1");
		else if(!a) puts(t==b?"2":"-1");
		else if(a==1){
			int X,Y;
			int g=exgcd(b,p,X,Y);
			int B=((LL)t-x+p)%p;
			printf("%d\n",B%g?-1:int(((LL)B/g*X%(p/g)+p/g)%(p/g)+1));//求解线性同余方程。
		}
		else{
			LL B=((LL)(a-1)*t+b)%p*qpow(((LL)(a-1)*x+b)%p,p-2,p)%p;
			int ans=BSGS(a,B,p);
			printf("%d\n",ans<0?-1:ans+1);
		}
	}
	return 0;
}