BZOJ 3122 SDOI2013 随机数生成器 数论 EXBSGS

题目大意:给定一个数列X(i+1)=(a*Xi+b)%p 求最小的i>0,使Xi=t

0.0 此题能1A真是太好了

首先讨论特殊情况

若X1=t ans=1

若a=0 ans=b==t?2:-1

若a=1 X1+b*(ans-1)==t (%p) 扩展欧几里得

temp=b/(a-1)

则有

(X(i+1)+temp)=a*(Xi+temp)

Xans=(X1+temp)*a^(ans-1)-temp

其中Xans%p=t

则有

(X1+temp)*a^(ans-1)-temp == t (%p)

两侧同乘a-1得

(a*X1-X1+b)*a^(ans-1) == (a-1)*t+b (%p)

Y=a*X1-X1+b

Z=a*t-t+b

Y*a^(ans-1) == Z(%p)

将Y和p约分

若Z%gcd(Y,p)!=0 return -1

A=a

B=Z*Y^-1

C=p

x=ans-1

A^x==B(%C)

然后就是EXBSGS的模板了0.0 这个模板我再也不想打第二遍了0.0

代码没法看了,诸位理解算法思想就好了0.0

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 1001001
using namespace std;
typedef long long ll;
typedef pair<ll,ll> abcd;
ll X,Y,Z,a,b,c,p,t,A,B,C,D;
ll hash_table[M],val[M],tim[M],tot;
int Hash(ll x)
{
	int pos=x%M;
	while(1)
	{
		if(tim[pos]!=tot)
			tim[pos]=tot,hash_table[pos]=-1,val[pos]=0x3f3f3f3f;
		if(hash_table[pos]==-1||hash_table[pos]==x)
			return hash_table[pos]=x,pos;
		else
			++pos,pos%=M;
	}
}
int Get_Hash(ll x)
{
	int pos=x%M;
	while(1)
	{
		if(tim[pos]!=tot)
			tim[pos]=tot,hash_table[pos]=-1;
		if(hash_table[pos]==-1)
			return -1;
		if(hash_table[pos]==x)
			return pos;
		else
			++pos,pos%=M;
	}
}
ll GCD(ll x,ll y)
{
	return y?GCD(y,x%y):x;
}
abcd EXGCD(ll x,ll y)
{
	if(!y) return abcd(1,0);
	abcd temp=EXGCD(y,x%y);
	return abcd(temp.second,temp.first-x/y*temp.second);
}
ll Inverse(ll x)
{
	ll temp=EXGCD(x,C).first;
	return (temp%C+C)%C;
}
ll Extended_Big_Step_Giant_Step()
{
	ll i,m,cnt=0,temp,base=1;
	int pos;
	if(B>=C)
		return -1;
	for(i=0,temp=1%C;i<=50;i++,temp*=A,temp%=C)
		if(temp==B)
			return i;
	D=1;
	while(temp=GCD(A,C),temp!=1)
	{
		if(B%temp)
			return -1;
		++cnt;
		B/=temp;
		C/=temp;
		D*=A/temp;
		D%=C;
	}
	B*=Inverse(D);B%=C;
	m=(ll)ceil(sqrt(C)+1e-5);
	++tot;
	for(i=0,temp=1%C;i<m;i++,temp*=A,temp%=C)
		pos=Hash(temp),val[pos]=min(val[pos],i);
	for(i=1,base=1%C;i<=m;i++,base*=A,base%=C);
	for(i=0,D=1%C;i<m;i++,D*=base,D%=C)
	{
		temp=EXGCD(D,C).first*B;
		temp=(temp%C+C)%C;
		pos=Get_Hash(temp);
		if(~pos)
			return i*m+val[pos]+cnt;
	}
	return -1;
}
ll Deal()
{
	ll temp;
	cin>>p>>a>>b>>X>>t;
	if(X==t) return 1;
	if(!a) return b==t?2:-1;
	if(a==1)
	{
		t+=p-X;t%=p;
		//b*(ans-1)==t (%p)
		temp=GCD(b,p);
		if(t%temp) return -1;
		b/=temp;t/=temp;p/=temp;
		temp=(EXGCD(b,p).first*t%p+p)%p;
		return temp+1;
	}
	Y=(a*X-X+b)%p;
	Z=(a*t-t+b)%p;
	temp=GCD(Y,p);
	if(Z%temp) return -1;
	Y/=temp;Z/=temp;p/=temp;
	C=p;
	B=Z*Inverse(Y)%p;
	A=a;
	temp=Extended_Big_Step_Giant_Step();
	if(temp==-1)
		return -1;
	return temp+1;
}
int main()
{
	int T;
	for(cin>>T;T;T--)
		cout<<Deal()<<endl;
}
时间: 2024-10-11 15:05:32

BZOJ 3122 SDOI2013 随机数生成器 数论 EXBSGS的相关文章

BZOJ 3122 SDOI2013 随机数生成器

一大堆边界一开始并不知道,胡乱判了几个之后一直WA 无奈之下只好去下载了数据,然后就疯狂判各种奇怪的边界了 刨去边界问题 首先我们考虑a=1的情况 x1+k*b=t(mod p) ex_gcd即可解 考虑a>1的情况 令S=X+b/(a-1) 原式就变成了一个等比数列 即S1*a^k=(t+b/(a-1))(mod p) 移项之后BSGS解即可 其他边界都可以O(1)判断 #include<cstdio> #include<cstring> #include<iostr

BZOJ 3122 [Sdoi2013]随机数生成器 BSGS

题意:链接 方法: BSGS 解析: 首先他给出了你数列在mod p意义下的递推式. 所以我们可以求出来通项. Xn+1+k=a?(Xn+k) 所以b=(a?1)?k 则我们可以解出来k 那么这个数列的通项是什么呢? Xn=an?1?(X1+k)?k 题中给定Xn 求出n就行了. 所以只需要移项就好了. 这里有个问题,此时我们的通项公式是不包含首项的,所以需要特判首项,另外还有第一项以外为常数项的时候. 代码: #include <cmath> #include <cstdio>

【bzoj3122】: [Sdoi2013]随机数生成器 数论-BSGS

[bzoj3122]: [Sdoi2013]随机数生成器 当a>=2 化简得 然后 BSGS 求解 其他的特判 : 当 x=t  n=1 当 a=1  当 a=0 判断b==t 1 /* http://www.cnblogs.com/karl07/ */ 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <map&

【BZOJ 3122】 [Sdoi2013]随机数生成器 (BSGS)

3122: [Sdoi2013]随机数生成器 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1442  Solved: 552 Description Input 输入含有多组数据,第一行一个正整数T,表示这个测试点内的数据组数. 接下来T行,每行有五个整数p,a,b,X1,t,表示一组数据.保证X1和t都是合法的页码. 注意:P一定为质数 Output 共T行,每行一个整数表示他最早读到第t页是哪一天.如果他永远不会读到第t页,输出-1. Sa

bzoj3122: [Sdoi2013]随机数生成器

3122: [Sdoi2013]随机数生成器 Description Input Output HINT $ 0 \leqslant a \leqslant P-1,0 \leqslant b \leqslant P-1,2 \leqslant P \leqslant 10^9 $ BSGS裸题 把线性递推式转化为通项公式为 $ x[n] = x1 * A^{n-1} + \frac {B*A^{n-1}} {A-1} = t $ 移项,得: $ (x1 + \frac {B} {A-1}) *

BZOJ 2875: [Noi2012]随机数生成器( 矩阵快速幂 )

矩阵快速幂...+快速乘就OK了 -------------------------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; typedef long long ll; ll MOD, a, c, x, n, g; ll MUL(ll a, ll b) { ll ans = 0; for(; b; b >>= 1

[bzoj 2875][noi2012]随机数生成器

传送门 Description 栋栋最近迷上了随机算法,而随机数是生成随机算法的基础.栋栋准备使用线性同余法(Linear Congruential Me thod)来生成一个随机数列,这种方法需要设置四个非负整数参数m,a,c,X[0],按照下面的公式生成出一系列随机 数X[n]X[n+1]=(aX[n]+c)mod m其中mod m表示前面的数除以m的余数.从这个式子可以看出,这个序列的下一个数 总是由上一个数生成的.用这种方法生成的序列具有随机序列的性质,因此这种方法被广泛地使用,包括常用

[Sdoi2013]随机数生成器(BSGS)

#include<map> #include<cmath> #include<cstdio> #include<iostream> #define ll long long using namespace std; inline int read(){ int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'

BZOJ 3671 NOI2014 随机数生成器 贪心+暴力

题目大意:.....有点长自己看吧 首先既然是排序后的序列字典序最小,那么一定要选尽量小的数字走 然后T是1~m*n的序列 所以不存在重复(一开始我居然把这个条件看漏了) 好的这题贪心 每次选择没有被标记的最小点,然后把左下方和右上方都标记掉(记得标记重复时break,不然就挂了) 注意5000*5000的数组开两个int就是极限了 开多了妥妥MLE 所以T数组记得重复利用 暴力跑了38秒 不过这题每一行能够选择的区域一定是连续的 可以对于每一行维护一个l和r 每次更新取最值即可 这个代码跑了2