BSGS学习笔记

这个算法非常傻逼。

PS:我不会Markdown 数学公式只能这么写,敬请谅解。

用来求高次同余方程:a^x=b(mod p)最小的x

通过费马小定理,我们能得知:a^k=a^(k mod (p-1)) (mod p)

当然,前提是gcd(a,p)>1并且p为质数。

证明:

a^(k mod (p-1))=a^(k-m(p-1))

\frac{a^k}{a^{(p-1)^m}}=a^k (mod p)

显然a^(p-1)^m mod p=1,虽然余数没有可除性,但是应该看得出来这个是成立的

然后我们就把x变成i*m-j

a^m^i=b*a^j(mod p)

枚举j,把右式插入map中,因为j越大,i*m-j越小。所以不考虑重复。

然后再枚举i,将左式的结果再map中查找,第一个就是答案.

推荐一道模板题,SDOI2011计算器,正好复习一下扩欧

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,jzsb,y,z,p;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(b==0){
		x=1,y=0,d=a;
		return ;
	}
	exgcd(b,a%b,d,x,y);
	ll t=x;x=y,y=t-(a/b)*y;
} 
map<ll,ll> jyk;
ll gcd(ll a,ll b){
	return b==0?a:gcd(b,a%b);
}
int main(){
	int i;
	cin>>t>>jzsb;
	while(t--){
		scanf("%lld%lld%lld",&y,&z,&p);
		if(jzsb==1){
			ll res=1;
			while(z){
				if(z&1)res=(res*y)%p;
				y=(y*y)%p;
				z>>=1;
			}	
			printf("%lld\n",res); 
		}if(jzsb==2){
			ll d,u,v;
			exgcd(y,p,d,u,v);
			if(z%d==0){
				printf("%lld\n",(u*(z/d)%(p/d)+(p/d))%(p/d));
			}else puts("Orz, I cannot find x!");
		}if(jzsb==3){
			if(gcd(y,p)>1)puts("Orz, I cannot find x!");
			else{
				jyk.clear();
				ll m=ceil(sqrt(p)),res=z%p,w=1;
				for(i=0;i<=m;i++){
					jyk[res]=i;
					res=(res*y)%p;
					if(i>=1)w=(w*y)%p;
				}
				ll h=1;
				int flag=0;
				for(i=1;i<=m;i++){
					h=(h*w)%p;
					if(jyk[h]){
						printf("%lld\n",((i*m-jyk[h])%p+(p))%(p)),flag=1;
						break;
					}
				}
				if(!flag)puts("Orz, I cannot find x!");
			}	
		}
	}
	return 0;
} 

  

就好了。

 

上一篇:洛谷P5520 青原樱(组合数学)


下一篇:[数论]拓展欧几里得算法