同余
费马小定理
\(a^{p}\equiv a\ \ \ (mod\ p)\) 其中 \(p\) 为素数且 \(a\) 不是 \(p\) 的倍数.
证明:
引理: \(\binom{p}{n}\ mod\ p=\frac{p!}{n!(p-n)!}\ mod\ p=0\ \ \ \ (n\ne0或p)\) . 正确性显然
由上柿可知:
将 \(b=a-1\) 代入:
证毕.
应用1 : 显然 , \(a^{p-2}\equiv\frac{1}{a}\ \ \ mod\ p\) 在模数为素数, \(a,p\) 互质时,可用费马小定理求逆元。
应用2: 素性检验。
BSGS
求解: \(a^{t}\equiv n\ \ \ (mod\ p)\)
解:
根据欧拉定理有:
即:
那么,若存在整数解 \(t\),则 \(t\in [0,\varphi (p)-1]\)
将区间 \([0,p]\) 分为 \(\sqrt p\) 段,每段长度为 \(k=\lfloor \sqrt p\ \rfloor+1\).
\(t=kx-y\ \ \ \ x\in[1,k]\ \ \ \ y\in[0,k-1]\),特判一下 \(t=0\) 的情况.
那么,有
即,
对于 \(y\in[0,k-1]\),计算 \(ba^{y}\ mod\ p\),存到 \(hash\) 表里。
然后计算 \(x\in[1,k]\) 的 \(a^{kx}\ mod\ p\) 判断\(hash\) 表里有没有这个值即可。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a,p,b;
int mod1=388211;
struct node
{
int val,hah;//val存原始数据,hah存要存的值
};
vector<node>hash1[388311];
inline int qr()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
inline LL poww(LL a,LL x)
{
LL ans=1;
a%=p;
while(x)
{
if(x&1)
ans=(ans*a)%p;
a=(a*a)%p;
x>>=1;
}
return ans%p;
}
inline void make_hash()//建hash表
{
int k=sqrt(p)+1;
for(register int i=0,j=b%p;i<k;i++)
{
node op=(node){j,i};//j是mod p之后的值,i表示 -y
hash1[j%mod1].push_back(op);
j=(LL)j*a%p;
}
}
inline void clear_hash()//清空hash表
{
int k=sqrt(p)+1;
for(register int i=0,j=b%p;i<k;i++)
{
if(hash1[j%mod1].size())
hash1[j%mod1].clear();
j=(LL)j*a%p;
}
}
inline LL BSGS(LL a,LL b,LL p)
{
if(1%p==b%p)
return 0;//特判 t=0
int k=sqrt(p)+1;
make_hash();
int ak=poww(a,k);
for(register int i=1,j=ak%p;i<=k;i++)//代入x
{
int siz=hash1[j%mod1].size();
if(siz)//如果这个hash值存过
{
LL ans=0;
for(register int c=0;c<siz;c++)//遍历这个hash值存的初始值
if(j==hash1[j%mod1][c].val)//初始值相同
ans=(LL)i*k-hash1[j%mod1][c].hah;//更新答案,因为-y是从小到大存入hash表里的,所以后面的答案可能会更优
if(ans)
return ans;
}
j=(LL)j*ak%p;
}
return -1; //无解返回-1
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
while(1)
{
a=qr();
p=qr();
b=qr();
if(!(a|b|p))
return 0;
LL res=BSGS(a,b,p);
if(res==-1)
printf("No Solution\n");
else
printf("%lld\n",res);
clear_hash();
}
//system("pause");
return 0;
}
扩展BSGS
求解: \(a^{t}\equiv n\ \ \ (mod\ p)\) \(a,p\) 不一定互质.
- \(a^0\equiv b\ \ \ (mod\ p)\) , 则 \(t=0\) .
- 设 \(d=(a,p)\) ,
\(d=1\) 时就是常规 \(BSGS\).
\(d>1\) 时 , \(d\mid b\)时有解 , \(d\nmid b\)时无解.
将原式两边同时除以 \(d\) , 得到
此时 \((\frac{a}{d},\frac{p}{d})=1\),故 \(\frac{a}{d}\) 的逆元一定存在,方程等价于:
观察此时 \(a\) 与 \(\frac{p}{d}\) 互质,直接用朴素版 \(BSGS\) 算法 , 反之再来一遍。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int a,p,b;
int mod=388211;
struct node
{
int val,hah;
};
vector<node>hash1[388211];
inline int qr()
{
int x=0,w=1;char ch=0;
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1;y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
inline int poww(int a,int x,int p)
{
int ans=1;
a%=p;
while(x)
{
if(x&1)
ans=(LL)ans*a%p;
a=(LL)a*a%p;
x>>=1;
}
return ans;
}
inline void make_hash(int a,int b,int p)//建立hash表
{
int k=sqrt(p)+1;
for(register int i=0,j=b%p;i<k;i++)
{
hash1[j%mod].push_back((node){j,i});
j=(LL)j*a%p;
}
}
inline void clear_hash(int a,int b,int p)//清空hash表
{
int k=sqrt(p)+1;
for(register int i=0,j=b%p;i<k;i++)
{
if(hash1[j%mod].size())
hash1[j%mod].clear();
j=(LL)j*a%p;
}
}
inline LL BSGS(int a,int b,int p)
{
make_hash(a,b,p);
int k=sqrt(p)+1;
int ak=poww(a,k,p);
for(register int i=1,j=ak;i<=k;i++)
{
int siz=hash1[j%mod].size();
if(siz)
{
int ans=0;
for(register int c=0;c<siz;c++)
if(j==hash1[j%mod][c].val)
ans=(LL)i*k-hash1[j%mod][c].hah;
if(ans)
{
clear_hash(a,b,p);
return ans;
}
}
j=(LL)j*ak%p;
}
clear_hash(a,b,p);//清空hash表
return -1;
}
int EXBSGS(int a,int b,int p)
{
b=(b%p+p)%p;
if(1%p==b%p)//特判t==0
return 0;
int x=0,y=0;
int d=exgcd(a,p,x,y);//求a,p最大公因子
if(d>1)
{
if(b%d)//b无法整除d无解
return -1;
exgcd(a/d,p/d,x,y);//求a/d逆元
return EXBSGS(a,(LL)b/d*x%(p/d),p/d)+1;
}
return BSGS(a,b,p);
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
while(1)
{
a=qr();
p=qr();
b=qr();
if(!(a|p|b))
return 0;
LL res=EXBSGS(a,b,p);
if(res==-1)
printf("No Solution\n");
else
printf("%lld\n",res);
}
//system("pause");
return 0;
}