思路
询问1:快速幂就可以了。
询问2:可以转化为求解同余方程
,使用扩展欧几里得就可以求解,在gcd(y,p)|z的时候有解。
询问3:重点要讲的BSGS算法(我习惯叫北上广深)。我们要求满足同余式的最小非负数x,暴力枚举x在[0,p)的范围内,在p非常大的情况下是会爆的。可以令x=mi-j,那么式子转化为了
。为什么要这样转换?
因为我们固定了m的情况下,i和j的取值都在,就可以把所有的x都表示出来,首先跑一遍j,用map存储所有
,然后再从小到大跑i,找到同余的情况直接跳出即可。复杂度开了个根号。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll fpow(ll a,ll b,ll mod){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1,y=0;
return a;
}
ll gcd=exgcd(b,a%b,y,x);
y-=a/b*x;
return gcd;
}
void bsgs(ll y,ll z,ll p){
z%=p; //可能板子不太好,所以加了很多特判
if(y%p==0){ //那么同余0
if(z){ //如果右边余不为0
cout<<"Orz, I cannot find x!"<<endl;
return;
}else{
if(p==1){ //如果模数为1
cout<<0<<endl;
return;
}else{
cout<<1<<endl;
return;
}
}
}
ll m=ceil(sqrt(p)); //开根号取整
map<ll,ll>mp;
ll now=z,f=fpow(y,m,p);
mp[now]=0;
for(int j=1;j<=m;j++){ //对于同余式右边
now=now*y%p;
mp[now]=j; //哈希记录zy^j
}
now=1;
for(int i=1;i<=m;i++){
now=now*f%p; //对同余式左边,(y^m)^i
if(mp[now]){ //找到了那个最小的x=m*i-j
cout<<((i*m-mp[now])%p+p)%p<<endl;
return;
}
}
cout<<"Orz, I cannot find x!"<<endl;
}
int t,k,y,z,p;
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>t>>k;
if(k==1){
for(int i=1;i<=t;i++){
cin>>y>>z>>p; //第一种直接用快速幂求就行了
cout<<fpow(y,z,p)%p<<endl;
}
}else if(k==2){
for(int i=1;i<=t;i++){
cin>>y>>z>>p; //第二种用拓展欧几里得
ll x,d;
ll gcd=exgcd(y,p,x,d);
if(z%gcd) cout<<"Orz, I cannot find x!"<<endl;
else{
ll tmp=p/gcd;
while(x<0) x+=tmp;
cout<<((x*z/gcd)%tmp+tmp)%tmp<<endl;
}
}
}else if(k==3){
for(int i=1;i<=t;i++){
cin>>y>>z>>p;
bsgs(y,z,p); //第三种用北上广深算法求
}
}
return 0;
}

京公网安备 11010502036488号