数论三连1·BSGS和EXBSGS

BSGS:用于求解a^x mod c=b。(gcd(a,c)==1)

设m=floor(sqrt(c)),则x=i*m-j;->a^(i*m) mod c=b*a^j mod c。

然后事先map/hash储存所有右边的值,枚举I看是否能找到等式即可。

然后i要从1开始,不然会出现诡异的负数。

那么EXBSGS是什么? 当a,c不互质时,a^(-j)没有意义,此时BSGS会失效,需要使用EXBSGS。

EXBSGS:a^x=a*(a^(x-1))。 化成方程以后两边同除gcd(a,c),然后再转回同余式,一直到gcd(a,c)=1为止。这样最多进行log n次。同时,可能出现解< log n的情况,直接枚举判断即可。

同余式和方程之间的转换是一种常用而重要的思想。

BSGS的板子:(bzoj2242)

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline LL read(){
	LL x=0; int w=0; char ch=0;
	while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
	while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return w? -x:x;
}
map<LL,LL> mat;
LL pw(LL a,LL b,LL P){
	LL res=1;
	while (b){
		if (b&1) res=(res*a)%P;
		b>>=1; a=(a*a)%P;
	}
	res=(res%P+P)%P;
	return res;
}
void solve_1(LL y,LL z,LL P){
	LL x=(pw(y,z,P)%P+P)%P; x=(x%P+P)%P;
	printf("%lld\n",x); return;
}
void No_Ans(){
	puts("Orz, I cannot find x!");
}
LL gcd(LL a,LL b){ return (!b)? a:gcd(b,a%b); }
void ex_gcd(LL a,LL b,LL c,LL &x,LL &y){
	if (!a){ x=0; y=c/b; return; }
	if (!b){ y=0; x=c/a; return; }
	ex_gcd(b,a%b,c,y,x);
	y-=(a/b)*x;
}
void solve_2(LL Y,LL z,LL P){   
	Y%=P; z%=P; LL c=gcd(Y,P); if (z%c) { No_Ans(); return; }
	Y/=c; P/=c; z/=c; LL x,y; ex_gcd(Y,P,1,x,y); 
	x=((x%P)*z)%P; x=(x%P+P)%P; printf("%lld\n",x); return;
}
//a^(i*blo-j)=b; a^(i*blo)=b*(a^j); (j<blo);
void solve_3(LL a,LL b,LL P){
    a%=P; b%=P; mat.clear(); LL blo=floor(sqrt(P)); 
    if ((!a)&&(b)) { No_Ans(); return; }
	rep(i,1,blo) mat[((b*pw(a,i-1,P))%P+P)%P]=i; 
	for(LL i=1; i*blo<=P+blo; i++){
		LL tmp=mat[pw(a,i*blo,P)]; 
		if (tmp){ tmp--; printf("%lld\n",i*blo-tmp); return; }
	}   
	No_Ans(); return;
}   
int main(){ 
	int cas=read(); int kd=read();
	rep(cl,1,cas){
		LL y=read(); LL z=read(); LL P=read();
		if (kd==1) solve_1(y,z,P); 
		if (kd==2) solve_2(y,z,P);
		if (kd==3) solve_3(y,z,P);
	}
	return 0;
}

EXBSGS的板子:(bzoj1467)

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline LL read(){
	LL x=0; int w=0; char ch=0;
	while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
	while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return w? -x:x;
}
LL pw(LL a,LL b,LL c){
	LL res=1;
	while (b){
		if (b&1) res=(res*a)%c;
		b>>=1; a=(a*a)%c;
	}
	return (res%c+c)%c;
}
LL gcd(LL a,LL b){ return (!b)? a:gcd(b,a%b); }
void exgcd(LL a,LL b,LL c,LL &x,LL &y){
	if (!a){ x=0; y=c/b; return; }
	if (!b){ y=0; x=c/a; return; }
	exgcd(b,a%b,c,y,x);
	y-=(a/b)*x;
}
struct EXBSGS{
	map<LL,LL> mat;
	LL tepan(LL A,LL B,LL C){
		rep(i,0,60) if (pw(A,i,C)==B) return i;
		return -1;
	}
	LL bsgs(LL A,LL B,LL C){ 
		mat.clear(); LL blo=floor(sqrt(C)); LL s=1;
		rep(i,0,blo-1) { mat[((s*(B%C))%C+C)%C]=i+1; s=((s*A)%C+C)%C; }
		for(LL i=1; i*blo<=C+blo; i++){
			LL tmp=mat[pw(A,i*blo,C)]; 
			if (tmp){ tmp--; return (i*blo-tmp); } 
		}
		return -1;
	}
	LL work(LL A,LL B,LL C){
		LL d=gcd(A,C); if (B%d) return -1; if (d==1) return bsgs(A,B,C); 
		LL t=C/d; LL x,y; exgcd(A/d,t,1,x,y); x=((x%t)+t)%t; 
		LL tmp=work((A%t+t)%t,(((B/d)*x)%t+t)%t,t); return (tmp==-1)? (-1):(tmp+1);
	}
	LL solve(LL A,LL B,LL C){
		A=(A%C+C)%C; B=(B%C+C)%C;  
		LL ans1=tepan(A,B,C); LL ans2=work(A,B,C);
		if (ans1==-1) return ans2; if (ans2==-1) return ans1;
		return min(ans1,ans2);
	}
}exbsgs;
int main(){
	for(;;){
		LL a=read(); LL c=read(); LL b=read();
		if ((!a)&&(!b)&&(!c)) break;
		LL ans=exbsgs.solve(a,b,c); 
		if (ans==-1) puts("No Solution"); 
		else printf("%lld\n",ans);
	}
	return 0;
}

UPD:bzoj4128 矩阵乘法+BSGS。其实是一样的。。感觉也不用矩阵求逆啊???

#include<bits/stdc++.h>
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline int read(){
	int x=0; int w=0; char ch=0;
	while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
	while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return w? -x:x;
}
const int N=71;
int n,P;
struct mat{ 
    int num[N][N]; 
    void init(){ ms(num,0,num);	}
    void one(){ init(); rep(i,1,n) num[i][i]=1; }
}a,b;
mat operator * (mat &a,mat &b){
	mat c; c.init();
	rep(k,1,n) rep(i,1,n) rep(j,1,n) c.num[i][j]=(c.num[i][j]+a.num[i][k]*b.num[k][j])%P;
	rep(i,1,n) rep(j,1,n) c.num[i][j]=(c.num[i][j]%P+P)%P;
	return c;
}
bool operator < (mat a,mat b){
	rep(i,1,n) rep(j,1,n) 
	if (a.num[i][j]!=b.num[i][j]) return a.num[i][j]<b.num[i][j];
	return 0;
}
bool operator == (mat a,mat b){
	rep(i,1,n) rep(j,1,n)
	if (a.num[i][j]!=b.num[i][j]) return 0;
	return 1;
}
map<mat,int> mp;
int bsgs(mat &a,mat &b){
	int m=floor(sqrt(P)); mat s; s.one(); mp[s]=1; 
	rep(i,1,m){ s=s*a; mp[s*b]=i+1; } mat c; c.one();
	rep(i,1,m+2){ c=c*s; int t=mp[c]; if (t>0) return i*m-(t-1); }
	return -1;
}
int main(){
	n=read(); P=read(); 
	rep(i,1,n) rep(j,1,n) a.num[i][j]=read();
	rep(i,1,n) rep(j,1,n) b.num[i][j]=read(); 
	int ans=bsgs(a,b); printf("%d\n",ans); 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/shiveringkonnyaku/article/details/82925844