第一次写一个比较高大上的数论题吧~~~虽然要花好长时间才明白一点,但是有收获吧~~对扩展欧几里得加深了理解。还认识了高大上的BSGS算法,哈哈~虽然还是一知半解,脑袋太小理解太慢,不易记住
上别人的题解吧~~~
相关连接:
BSGS算法_Baby steps giant steps算法(无扩展)详解
bzoj2242: [SDOI2011]计算器 && BSGS 算法
扩展欧几里德算法求解线性同余方程
http://blog.csdn.net/lwfcgz/article/details/8887906BSGS[bzoj2242][bzoj3122]
关于第三问一些好的解析:(1)baby step giant step,意为先小步后大步。由费马小定理可得答案不会超过p,我们可以把答案X看作k*m+i的形式,显然当m=sqrt(p)时复杂度最优。预处理m以内的hash值存入map(今天发现map真是个好东西), 枚举k,因为ni(y^k*m)*z%p=y^i%p,当当前值存在于哈希表是,则说明找到一个合法k与合法i,计入答案。
此时有一个特殊情况,若当前hash值为一,即ni(y^k*m)===z(mod p),则i=0;
(2)
第三问高次同余方程a^x=b(mod n),Baby Step Giant Step算法::
解高次同余方程a^x=b(mod n) n为质数
设x=i*m+j m=ceil(sqrt(n))
则a^im*a^j=b(mod n)
==>(a^m)^i*a^j=b(mod n)
求a^m模n的乘法逆元v=a^(n-m-1){
乘法逆元:若ax=1(mod n),则称a模n的乘法逆元是x
性质:K/x mod n = K*a mod n (将就着看,不是很科学)
证明v=a^(n-m-1)是乘法逆元:{
由费马小定理得a^(n-1)=1(mod n)
则a^m*a^(n-m-1)=1(mod n)
所以v=a^(n-m-1)
}
}
所以(a^m)^i*a^j=b(mod n)
==>a^j=b*v^i(mod n)
枚举j=0 ->m-1 将a^j mod n存入hash表
枚举i=0 ->m-1 每次计算 b*v^i mod n,若计算过程中发现b*v^i mod n在hash表中出现过,返回i*m+j
这样就解完了复杂度O(sqrt(n))当然这是你hash写得好才行,蒟蒻用map,或者二分判断,复杂度O(sqrt(n)logn)
复杂度O(sqrt(n)log(n))来自快速幂和hash
(3)
给定y、z、p,计算满足yx mod p=z的最小非负整数x。p为质数(没法写数学公式,以下内容用心去感受吧)
设 x = i*m + j.
则 y^(j)≡z∗y^(-i*m)) (mod p)
则 y^(j)≡z∗ine(y^(i*m)) (mod p)(逆元)
由费马小定理y^(p-1)≡1 (mod p) 得 ine(y^m) = y^(p-m-1)
ine(y^(i*m)≡ine(y^((i−1)m))∗y^(p-m-1)
1.首先枚举同余符号左面,用一个hash保存(y^j,j),因为j可能等于0,所以hash[1]要赋为一个特殊值。
2.再枚举同余符号右面,如果hash(z∗ine(y^(i*m)))存在,就找到了一组解。
显然,m=sqrt(p)的时候复杂度最低为O(sqrt(p)),m=ceil(sqrt(p)).
革命尚未成功,同志仍需努力!
#include<cstdio>
#include<iostream>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
typedef long long ll;
map<ll,ll>hash;
ll y,z,p;
ll power(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){
ll res,t;
if(b==0){
x=1;y=0;
return a;
}
res=exgcd(b,a%b,x,y);
t=x;x=y;y=t-(a/b)*y;
return res;
}
ll solve1(){
cout<<power(y,z,p)<<endl;
}
ll solve2(){
ll xx,yy,ans,d;
d=exgcd(y,p,xx,yy);
if(z%d!=0)
cout<<"Orz, I cannot find x!"<<endl;
else{
ans=(z/d)*xx;
ans=(ans%p+p)%p; //求出的ans可能为负数,这里求出的是最小的非负解
cout<<ans<<endl;
}
}
ll solve3(){
y%=p;z%=p;
if(!y&&!z)
cout<<"1"<<endl;
else if(!y){
cout<<"Orz, I cannot find x!"<<endl;
}
else{
ll m,v,e,res;
m=ceil(sqrt(p));v=power(y,p-m-1,p);e=1;
hash.clear();
hash[1]=m+1; //特殊情况
for(ll i=1;i<=m;i++){
e=e*y%p;
if(!hash[e])
hash[e]=i;
}
res=-1;
for(ll i=0;i<=m;i++){
if(hash[z]){
res=i*m+(hash[z]==m+1?0:hash[z]);
break;
}
z=z*v%p;
}
if(res==-1)
cout<<"Orz, I cannot find x!"<<endl;
else
cout<<res<<endl;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int t,k;
cin>>t>>k;
for(int i=0;i<t;i++){
cin>>y>>z>>p;
//cout<<y<<z<<p;
if(k==1) solve1();
else if(k==2) solve2();
else solve3();
}
}