扩展大步小步法(exBSGS)

从昨天调到今天,刚调过总结一下。
exBSGS是解决\(a^{l}\equiv b(\mod p)(\gcd(a,p)\ne 1)\)求最小非负整数\(l\)的问题。
\(a^{l-1}\times a\equiv b(\mod p)\)
$a^{l-1}\times \frac{a}{\gcd(a,p)}\equiv \frac{b}{\gcd(a,p)}(\mod \frac{p}{\gcd(a,p)}) $
\(d\)使\(\gcd(a,\frac{p}{d})=1\)\(\frac{a^{cnt}}{d}\equiv \frac{b}{d}(\mod \frac{p}{d})\)
\(a^{l-cnt}\times \frac{a^{cnt}}{d}\equiv \frac{b}{d}(\mod \frac{p}{d})\) 保证\(d\mid b\)
然后就用BSGS解决就可以了。
题目:洛谷P4195
代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<unordered_map>
using namespace std;
inline int kd(){
	unsigned int x=0;
	unsigned int f=1;
	char a=getchar();
	while(a<'0'||a>'9'){
		if(a=='-'){
			f=-1;
		}
		a=getchar();
	}
	while(a>='0'&&a<='9'){
		x=x*10+a-'0';
		a=getchar();
	}
	return x*f;
}
unsigned int a,b,p;
inline unsigned int gcd(unsigned int x,unsigned int y){
	if(y==0){
		return x;
	}
	return gcd(y,x%y);
}
inline unsigned int ksm(unsigned int x,unsigned int y){
	unsigned int ans=1;
	while(y){
		if(y&1){
			ans=1ll*ans*x%p;
		}
		x=1ll*x*x%p;
		y>>=1;
	}
	return ans;
}
int main(){
	a=kd();p=kd();b=kd();
	while(a&&b&&p){
		unordered_map<unsigned int,unsigned int> f;
		unsigned int cun1,cun2;
		b%=p;
		a%=p;
		if(b==1||p==1){
			printf("0\n");
			a=kd();p=kd();b=kd();
			continue;
		}
		unsigned int g=gcd(a,p);
		unsigned int cnt=0;
		unsigned int zong=1;
		while(g!=1){
			if(b%g){
				printf("No Solution\n");
				break;
			}
			cnt++;
			b/=g;
			p/=g;
			zong=1ll*zong*(a/g)%p;
			if(zong%p==b%p){
				printf("%d\n",cnt);
				break;
			}
			g=gcd(a,p);
		}
		if(g!=1){
			a=kd();p=kd();b=kd();
			continue;
		}
		b%=p;
		a%=p;
		unsigned int m=sqrt(p);
		m+=(m*m!=p);
		unsigned int ans=1000000000;
		unsigned int t;
		cun1=zong;
		cun2=ksm(a,m);
		for(unsigned int i=1;i<=m;i++){
			cun1=1ll*cun1*cun2%p;
			f[cun1]=0;
		}
		t=b;
		for(unsigned int i=0;i<m;i++){
			f[b]=0;
			b=1ll*b*a%p;
		}
		b=t;
		cun1=zong;
		for(unsigned int i=1;i<=m;i++){
			cun1=1ll*cun1*cun2%p;
			if(f[cun1]==0){
				f[cun1]=i;
			}
		}
		for(unsigned int i=0;i<m;i++){
			unsigned int w=f[b];
			b=1ll*b*a%p;
			if(w){
				ans=min(ans,w*m-i+cnt);
			}
		}
		if(ans==1000000000){
			printf("No Solution\n");
		}
		else{
			printf("%lld\n",ans);
		}
		a=kd();p=kd();b=kd();
	}
	return 0;
}
posted @ 2023-01-16 19:37  zzzzzz2  阅读(33)  评论(0编辑  收藏  举报