#费马小定理,BSGS#BZOJ 3285 离散对数解指数方程

题目

求最小的正整数 \(x\) 满足 \(g^{ax+b}\equiv c\pmod p\)
其中 \(p\) 是一个质数, \(g,a,b,c\leq 10^{1000000},p\leq 2^{31}\)


分析

先将 \(g^a\)\(g^b\) 用费马小定理求出来,

问题就转换成 \(b'\times {a'}^x\equiv c'\pmod p\)

这个直接套用 BSGS 就可以了


代码

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#define rr register
using namespace std;
const int p=70001; typedef long long lll;
const int N=1000011; char A[N],B[N],C[N],G[N];
lll mod,lA,lB,lC,ans,lG,a,b,c,g;
struct Linked_Hash{
	struct node{int y,w,next;}E[p]; int Et,hs[p];
	inline void Clear(){Et=0,memset(hs,-1,sizeof(hs));}
	inline void Insert(int w,int x){E[++Et]=(node){x,w,hs[w%p]},hs[w%p]=Et;}
	inline signed locate(int W){
		for (rr int i=hs[W%p];~i;i=E[i].next)
		    if (E[i].w==W) return E[i].y;
		return -1;
	}
}ha;
inline lll gcd(lll x,lll y){return y?gcd(y,x%y):x;}
inline lll exBSGS(lll a,lll c,lll b,lll mod){
	ha.Clear();
	rr lll GCD=gcd(a,mod),t=1;
	rr lll CNT=0,ir=sqrt(mod)+1;
	while (GCD>1){
		if (b%GCD||c%GCD) return -1;
		b/=GCD,c/=GCD,mod/=GCD,
		t=t*(a/GCD)%mod,
		GCD=gcd(a,mod),++CNT;
		if (b==t*c%mod) return CNT;
	}
	rr lll now=1;
	for (rr int i=0;i<ir;++i)
	    ha.Insert(now*b%mod,i),now=now*a%mod;
	a=now,now=t;
	if (!a) return !b?1:-1;
	for (rr int i=0;i<=ir;++i){
		rr int j=ha.locate(now*c%mod);
		if (j>=0&&i*ir+CNT>j) return i*ir+CNT-j;
		now=now*a%mod;
	}
	return -1;
}
inline lll ksm(lll x,lll y){
	rr lll ans=1;
	for (;y;y>>=1,x=x*x%mod)
	    if (y&1) ans=ans*x%mod;
	return ans; 
}
signed main(){
	scanf("%s%s%s%s%lld",A+1,B+1,C+1,G+1,&mod);
	lA=strlen(A+1),lB=strlen(B+1),lC=strlen(C+1),lG=strlen(G+1);
	for (rr int i=1;i<=lG;++i) g=(g*10+G[i]-48)%mod;
	for (rr int i=1;i<=lC;++i) c=(c*10+C[i]-48)%mod;
	for (rr int i=1;i<=lA;++i) a=(a*10+A[i]-48)%(mod-1);
	for (rr int i=1;i<=lB;++i) b=(b*10+B[i]-48)%(mod-1);
	a=ksm(g,a),b=ksm(g,b),ans=exBSGS(a,b,c,mod);
	if (ans==-1) printf("no solution");
	    else printf("%lld",ans);
	return 0;
}
posted @ 2021-09-25 07:39  lemondinosaur  阅读(48)  评论(0编辑  收藏  举报