求解关于x的方程
a^x=z(mod p),其中gcd(a,p)=1.
做法的话并不难,但是要搞懂细节还蛮多的。
bsgs算法是这样的:x可以写成i*m-j的形式(这里m取值随意,但是取√p上取整时跑的最快)
a^(im-j)≡z(mod p)
推得 a^im≡z*(a^j)
那么我们枚举j的值从0--(m-1),将算出的z*(a^j)加入map里面
再枚举i从1-m,将每次算出的a^im的结果去map中匹配
若匹配到,x=i*m-j。
细节
1.i的范围从1到m
证明:
a ^ x Ξ b (mod p)
a ^ (p-1) Ξ 1 (mod p)
若x大于等于p-1
则a^xΞa^(p-1) * a^(x-p+1)Ξa^(x-p+1)
故x小于p-1
又m=√p
故i从1到m
得证
2.为什么bsgs在跳步过程中没有漏解
原因:在枚举i的过程中,看似是跳过了好多情况,但是其实在计算了每一个i之后,都会去map里面找每一个j去对应,相当于是将每一个i所对应的余数(剩余系)都枚举了一遍,即枚举了一遍完整的x的范围(因为x=i*m-j)。
模板题https://www.luogu.org/problemnew/show/P2485
洛谷p2485 计算器
bsgs裸题
直接粘代码
CODE!
#include<bits/stdc++.h> #define ll long long using namespace std; ll t,k,y,z,p; map<ll,ll>a; ll fpow(ll a,ll b,ll p) { ll ret=1; while(b) { if(b&1) ret=(ret*a)%p; b>>=1; a=(a*a)%p; } return ret; } void exgcd(ll a,ll b,ll &gcd,ll &xx,ll &yy) { if(!b) { xx=1,yy=0; gcd=a; return; } exgcd(b,a%b,gcd,xx,yy); ll tr=xx; xx=yy; yy=tr-a/b*yy; } int main() { cin>>t>>k; if(k==1) { while(t--) { scanf("%lld%lld%lld",&y,&z,&p); printf("%lld\n",fpow(y,z,p)); } } else if(k==2) { while(t--) { scanf("%lld%lld%lld",&y,&z,&p); ll gcd,xx,yy; exgcd(y,p,gcd,xx,yy); if(z%gcd!=0) printf("Orz, I cannot find x!\n"); else { xx*=z/gcd; xx%=(p/gcd); printf("%lld\n",(xx+(p/gcd))%(p/gcd)); } } } else { while(t--) { scanf("%lld%lld%lld",&y,&z,&p); ll m=ceil(sqrt(p)); if(y%p==0&&z) {printf("Orz, I cannot find x!\n");continue;}; a.clear(); ll now=z%p,f=fpow(y,m,p); a[now]=0; for(ll i=1;i<=m;i++) { now=(now*y)%p; a[now]=i; } now=1; ll flag=0; for(ll i=1;i<=m;i++) { now=(now*f)%p; if(a[now]) { ll ans=(i*m-a[now])%p; printf("%lld\n",(ans+p)%p); flag=1; break; } } if(!flag) printf("Orz, I cannot find x!\n"); } } return 0; }
ps.不开long long见祖宗可还行
原文地址:https://www.cnblogs.com/oierglh/p/11234686.html
时间: 2024-11-08 04:09:19