bzoj3122: [Sdoi2013]随机数生成器

本文针对洛谷3122题提供详细的解析及代码实现,通过数学推导和算法优化,解决了题目中的递推问题。文章重点介绍了如何避免使用逆元导致的精度损失,并采用BSGS算法求解特定的离散对数问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3122

我去。。做这道题快把我累死了。。

原递推式:


推导过程:


为啥复制粘贴过来画质变渣了。。

另外需要注意的一点就是在做除法的时候不能直接用逆元,

比如说(10p/2p)mod p 本来应该等于5,但是你用逆元的话就成了0,因此应该一上来先把分子分母中公共的p除尽,再用逆元

代码:

#include <cstdio>
#include <map>
#include <cmath>
#define ll long long
using namespace std;
map<ll,ll> hash;
void exgcd(ll a, ll b, ll &x, ll &y)
{
	if(a%b==0){x=0;y=1;return;}
	ll xx, yy;
	exgcd(b,a%b,xx,yy);
	x=yy;y=xx-a/b*yy;
}
ll inv(ll a, ll p)
{
	ll x, y;
	exgcd(a,p,x,y);
	return (x%p+p)%p;
}
ll BSGS(ll A, ll B, ll p)
{
	ll i, j, m, t, Am, Aj;
	map<ll,ll>::iterator it;
	m=sqrt(p);
	hash.clear();
	for(i=0,t=1;i<=m;i++,t=(t*A)%p)
		if(hash.find(t)==hash.end())hash[t]=i;
	for(i=1,Am=1;i<=m;i++)Am=Am*A%p;
	if(Am==0)return -2;
	for(i=0,t=1;i<=m;i++,t=t*Am%p)
	{
		Aj=B*inv(t,p)%p;
		it=hash.find(Aj);
		if(it!=hash.end())return m*i+it->second;
	}
	return -2;
}
int main()
{
	ll x1, a, b, p, t, T, m, n, ans;
	while(~scanf("%lld",&T))
	{
		while(T-->0)
		{
			scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&x1,&t);
			if(a==0)
			{
				if(x1==t)ans=0;
				else if(b%p==t)ans=1;
				else ans=-2;
			}
			else if(a!=1)
			{
				m=t*(a-1)+b;
				n=x1*(a-1)+b;
				while(m%p==0 and n%p==0 and m)m/=p,n/=p;
				ans=BSGS(a,m%p*inv(n,p)%p,p);
				if(n==0 and m==0)ans=0;
			}
			else if(b!=0)
			{
				m=t-x1;
				n=b;
				while(m%p==0 and n%p==0 and m)m/=p,n/=p;
				m=(m%p+p)%p;
				ans=m*inv(n,p)%p;
				if(m==0 and n==0)ans=0;
			}
			else
			{
				if(x1==t)ans=0;
				else ans=-2;
			}
			printf("%lld\n",ans+1);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值