Baby-Step-Giant-Step(大步小步法,北上广深,不是个事,拔山盖世),简称 BSGS,使用分块思想解决离散对数问题。
形如 的同余式,可产生以下三种问题:
- 已知 ,求 ,是快速幂问题。
- 已知 ,求 ,是离散对数问题。
- 已知 ,求 ,是高次剩余问题。
我们现在可以利用扩欧和快速幂解决问题 1;而利用 BSGS 和 exBSGS,我们也可以解决问题 2 了!
BSGS
BSGS 求解 中 的值。要求 。
下面的代码时间复杂度为 。
//https://www.luogu.com.cn/problem/P3846
#include<bits/stdc++.h>
using namespace std;
namespace _yz
{
typedef long long ll;
ll p,b,n,siz,cnt;
map<ll,ll>check;
ll BSGS(ll a,ll c,ll p)
{
ll tmp=c%p,jud=1;
for(ll i=0;i<siz;i++)
{
check[tmp]=i;
tmp=a*tmp%p; jud=jud*a%p;
}
tmp=jud;
for(ll i=1;i<=cnt;i++)
{
if(check.count(jud)) return i*siz-check[jud];
jud=jud*tmp%p;
}
return -1;
}
void main()
{
ll tmp;
scanf("%lld%lld%lld",&p,&b,&n);
siz=sqrt(p-1); cnt=((p-1)/siz+((p-1)%siz?1:0));
tmp=BSGS(b,n,p);
if(tmp==-1) printf("no solution\n");
else printf("%lld\n",tmp);
return;
}
}
int main()
{
_yz::main();
return 0;
}
/*
a^b = c (mod p)
a^{m*siz-k} = c (mod p)
a^{m*siz} = c*a^k (mod p)
(k: 0 ~ size-1)
*/
exBSGS
exBSGS 也是用于求解 中 的值。不要求 。
显然,若 ,一个可行的解为 。
若 ,而 ,一个可行的解为 。(注意特判)
无解的充分条件是: 不是 的约数,且 。
证明
若 ,则令 。
则有
根据裴蜀定理,将 和 看作未知数,显然 或 ,上面的不定方程有解,原方程()有可能有解。
所以原方程可写成:
反复迭代,最终得到 ,其中 。显然 存在逆元,BSGS 可求。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace _yz
{
typedef long long ll;
ll a,b,p;
map<ll,ll>check;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1; y=0;
return;
}
exgcd(b,a%b,x,y);
ll tmp=x;
x=y; y=tmp-(a/b)*y;
}
ll inv(ll a,ll b)
{
ll x,y;
exgcd(a,b,x,y);
return (x%b+b)%b;
}
ll BSGS(ll f,ll a,ll b,ll p,ll c)
{
ll siz,cnt,t1,t2=1;
t1=b=b*inv(f,p)%p; siz=sqrt(p-1);
if(p==1||b==1) return c;
cnt=(p-1)/siz+((p-1)%siz?1:0);
for(ll i=0;i<siz;i++)
{
check[t1]=i;
t1=t1*a%p; t2=t2*a%p;
}
t1=t2;
for(ll i=1;i<=cnt;i++)
{
if(check.count(t1)) return siz*i-check[t1]+c;
t1=t1*t2%p;
}
return -1;
}
ll exBSGS(ll f,ll a,ll b,ll p,ll step)
{
ll d=gcd(a,p);
if(d==1) return BSGS(f,a,b,p,step);
if(f%p==b%p) return step;
if(b%d) return -1;
return exBSGS(a/d*f%p,a,b/d,p/d,step+1);
}
void main()
{
ll tmp;
while(true)
{
scanf("%lld%lld%lld",&a,&p,&b);
if(a==0&&b==0&&p==0) return;
check.clear();
a%=p; b%=p;
if(p==1 || (p!=1&&b==1))
{
printf("0\n");
continue;
}
tmp=exBSGS(1,a,b,p,0);
if(tmp==-1) printf("No Solution\n");
else printf("%lld\n",tmp);
}
return;
}
}
int main()
{
_yz::main();
return 0;
}
例题
P2485 [SDOI2011]计算器。显然是 sb 三合一。
#include<bits/stdc++.h> using namespace std; namespace _yz { typedef long long ll; ll y,z,p; map<ll,ll>check; ll pow(ll a,ll b,ll p) { ll ret=1; while(b) { if(b%2) ret=ret*a%p; a=a*a%p; b/=2; } return ret; } void exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return; } exgcd(b,a%b,x,y); ll tmp=x; x=y; y=tmp-(a/b)*y; } ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} void solve() { ll x,q,d=gcd(y,p); if(z%d) { printf("Orz, I cannot find x!\n"); return; } exgcd(y,p,x,q); printf("%lld\n",(x%p+p)%p*(z/d)%p); return; } void BSGS(ll a,ll b,ll p) { ll siz=sqrt(p),t1=b%p,t2=1; ll cnt=p/siz+(p%siz?1:0); if(t1==1) { printf("0\n"); return; } if(a%p==0&&t1==0) { printf("1\n"); return; } if(a%p==0&&t1) { printf("Orz, I cannot find x!\n"); return; } check.clear(); for(ll i=0;i<siz;i++) { check[t1]=i; t1=t1*a%p; t2=t2*a%p; } t1=t2; for(ll i=1;i<=cnt;i++) { if(check.count(t1)) { printf("%lld\n",i*siz-check[t1]); return; } t1=t1*t2%p; } printf("Orz, I cannot find x!\n"); return; } void main() { ll T,k; scanf("%lld%lld",&T,&k); while(T--) { scanf("%lld%lld%lld",&y,&z,&p); if(k==1) printf("%lld\n",pow(y,z,p)); else if(k==2) solve(); else BSGS(y,z,p); } return; } } int main() { _yz::main(); return 0; }
P3306 [SDOI2013] 随机数生成器。推推式子。
#include<bits/stdc++.h> using namespace std; namespace _yz { typedef long long ll; ll p,a,b,x1,t; map<ll,ll>check; ll pow(ll a,ll b) { ll ret=1; a%=p; while(b) { if(b%2) ret=ret*a%p; a=a*a%p; b/=2; } return ret; } ll inv(ll a){return pow(a,p-2);} void exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return; } exgcd(b,a%b,x,y); ll tmp=x; x=y; y=tmp-(a/b)*y; } ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} void BSGS(ll a,ll b,ll p) { ll siz=sqrt(p),t1=b%p,t2=1; ll cnt=p/siz+(p%siz?1:0); check.clear(); for(ll i=0;i<siz;i++) { check[t1]=i; t1=t1*a%p; t2=t2*a%p; } t1=t2; for(ll i=1;i<=cnt;i++) { if(check.count(t1)) { printf("%lld\n",i*siz-check[t1]+1); return; } t1=t1*t2%p; } printf("-1\n"); return; } void main() { ll T,tmp,t1,t2; scanf("%lld",&T); while(T--) { scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&x1,&t); if(x1==t) printf("1\n"); else if(a==0) { if(b==t) printf("2\n"); else printf("-1\n"); } else if(a==1) { if(b==0) { if(x1==t) printf("1\n"); else printf("-1\n"); } else { tmp=gcd(b,p); if((t-x1+b)%tmp) {printf("-1\n");continue;} exgcd(b,p,t1,t2); tmp=(t1*(t-x1+b)/tmp%p+p)%p; printf("%lld\n",tmp?tmp:p); } } else if((x1*(a-1)+b)%p==0) { if((t*(a-1)+b)%p==0) printf("1\n"); else printf("-1\n"); } else BSGS(a,(t*(a-1)+b)%p*inv(x1*(a-1)+b)%p,p);//取模要彻底啊亲 } return; } } int main() { _yz::main(); return 0; } /* DBSL Sn = a+ap+...+ap^n pSn = ap+ap^2+...+ap^{n+1} = Sn+ap^{n+1}-a Sn=(ap^{n+1}-a)/(p-1) */ /* a=0 x2 = b x3 = b xi = b */ /* a=1,b=0 x2 = x xi = x */ /* a=1,b!=0 x2 = x+b x3 = x+2b xi = x+(i-1)b t = x+(i-1)b (mod p) t = x+ib-b+kp ib+kp = t-x+b */ /* x(a-1)+b = 0 (mod p) t(a-1)+b = 0 (mod p) */ /* normal x2 = ax+b x3 = a(ax+b)+b = a^2x+ab+b x4 = a(a^2x+ab+b)+b = a^3x+a^2b+ab+b x5 = a(a^3x+a^2b+ab+b)+b = a^4x+a^3b+a^2b+ab+b xi = a^{i-1}x + /prod_{j=0}^{i-2}a^jb = a^{i-1}x + (a^{i-1}b-b)/(a-1) = a^{i-1}x + a^{i-1}b/(a-1) - b/(a-1) = a^{i-1}[x+b/(a-1)] - b/(a-1) a^{i-1} = [t+b/(a-1)]*[x+b/(a-1)]^{-1} (mod p) a^{i-1} = [t(a-1)+b]*[x(a-1)+b]^{-1} (mod p) */
本文作者:Yurchiu
本文链接:https://yz-hs.github.io/2ab979d324b5/
版权声明:本博客中所有原创文章除特别声明外,均允许规范转载,转载请注明出处。所有非原创文章,按照原作者要求转载。