标签:这一 意义 isp 自己 step prim log 因此 str
int prime[maxn],tot=0;
bool vis[maxn];
void init(int n)
{
vis[1]=1;
for(int i=2;i<=n;++i)
{
if(!vis[i]) prime[++tot]=i;
for(int j=1;j<=tot&&prime[j]*i<=n;++j)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0) break;//每个数都只会被它最小的素数因子筛掉
}
}
}//线性筛素数
#define ll long long
ll gcd(ll a,ll b)
{
return b==0?a:gcd(b,a%b);
}
int exgcd(int a,int b,int &d,int &x,int &y)
{
if(!b)
{
x=1,y=0;
return d=a;
}
else
{
exgcd(b,a%b,d,y,x);
y-=x*(a/b);
}
}//可用于求ax+by=d的一组解,当且仅当d=gcd(a,b)时有解(若要通过此特解推出其他解,每次x减小b/d,y增加a/d即可)
? 扩展欧几里得可以求逆元(逆元或许可以理解为在模意义下的倒数)
若要求a在模b意义下的逆元,首先a在模b意义下有逆元的前提条件是gcd(a,b)=1,即a和b互质,我们假设a在模b意义下的逆元为x,则ax在模b时同余于1,即可得ax+by=1(应是ax=by+1,移项后就变为了ax-by=1,我们令b=-b,则ax+by=1)
#define LL long long
LL pow_mod(LL a,LL b,int mod)
{
LL ans=1;
while(b)
{
if(b&1)
{
ans=(ans*a)%mod;
}
a=(a*a)%mod;
b>>=1;
}
}
\[ \varphi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})\cdots(1-\frac{1}{p_k}) \]
给出正整数n的唯一分解式
\[
n=p_1^{a_1}p_2^{a_2}p_3^{a_3}\cdots p_k^{a_k}
\]
用容斥原理,首先从总数n中减去p1,p2,p3...pk的倍数的个数(pi都是素数,故不是其倍数即为与其互质),即n-n/p1-n/p2...-n/pk(设pi最大的满足小于等于n的倍数为pi*t,故求得t为n/pi),然后加上“同时是两个数的倍数的数,再减去同时是三个数的倍数的数,...,最后得到的公式即为:
\[
\varphi(n)=\sum_{S\subseteq p_1,p_2,...p_k}(-1)^{|s|}\frac{n}{\prod_{p_i\epsilon S}}p_i
\]
int phi[maxn];
void init(int n)
{
phi[1]=1;
for(int i=2;i<=n;++i)
{
phi[i]=i;
}
for(int i=2;i<=n;++i)
{
if(phi[i]==i)
{
for(int j=i;j<=n;j+=i)
{
phi[j]=phi[j]/i*(i-1);
}
}
}
}//筛表法求欧拉函数
通俗的说,模n的完全剩余系就是{0,1,2,... ,n-1},而简化剩余系(也称缩系)就是完全剩余系中与n互素的那些数。
模n的完全剩余系最常见的写法是Z/nZ,也可以写成:
\[
Z/n或者Z_n
\]
,缩系记为
\[
Z_n^*
\]
#define LL long long
LL inv(LL a,LL mod)
{
LL d,x,y;
return exgcd(a,mod,d,x,y)==1?(x+mod)%mod:-1;
}//模乘法的逆
求逆的另一方法是利用欧拉定理
给定任意的整数n>1,对于n的缩系中的任意一个元素a,
\[
a^{\varphi(n)}\equiv1(mod\quad n)
\]
因此a的逆元就是
\[
a^{\varphi(n)-1}mod\quad n
\]
如果n是素数,则
\[
\varphi(n)=n-1
\]
所以a的逆就是pow_mod(a,n-2,n)
\[ ax\equiv b(mod\quad n) \]
把它转化为ax-ny=b,当d=gcd(a,n)不是b的约数时无解,否则两边同时除以d,得到a‘x-n‘y=b‘,a‘=a/d,n‘=n/d,b‘=b/d,即为
\[
a'x\equiv b'(mod\quad n')
\]
此时a‘和n‘已经互素,因此再左乘a‘在模n‘意义下的逆元,则解为:
\[
x\equiv (a')^{-1}b'(mod\quad n')
\]
这个解是模n‘剩余系中的一个元素,但我们还需要把它表示为模n剩余系中的元素。令(a‘)^-1 b‘=p,上述解相当于x=p,x=p+n‘,x=p+2n‘,x=p+3n‘,...。对于模n来说,假设p+in‘和p+jn‘同余,则(p+in‘)-(p+jn‘)=(i-j)n‘是n的倍数,因此,(i-j)是d(gcd(a,n))的倍数。换句话说,再模n剩余系下,
\[
ax\equiv b(mod\quad n)
\]
恰好有d个解,为p,p+n‘,p+2n‘,p+3n‘,...,p+(d-1)n‘。
如果有多个方程,变量还是只有一个,又该怎么做呢?
那就用到了下面的定理
假设有方程组
\[
x\equiv a_i(mod\quad m_i)
\]
且所有的mi两两互素。令M为所有mi的乘积,wi=M/mi,则gcd(wi,mi)=1。
用扩展欧几里得算法可以找到pi和qi使得wi * pi+mi * qi=1。然后令ei=wi*pi,则方程组等价于单个方程
\[
x\equiv e_1a_1+e_2a_2+\cdots +e_na_n(mod\quad M)
\]
,即在模M剩余系下,原方程组有唯一解。
证明:把等式wi pi+mi qi=1两边模mi后立即可得
\[
e_i\equiv 1(mod\quad m_i)
\]
,而对于所有不等于i的j,wi是mj的倍数,因此
\[
e_i\equiv 0(mod\quad m_j)
\]
,这样,x0对mi取模时,除了eiai这一项余数为1*ai=ai之外,其余项的余数均为0。
#define LL long long
LL crt(int n,int* a,int* m)
{
LL M=1,d,x,y,x=0;
for(int i=1;i<=n;++i) M*=m[i];
for(int i=1;i<=n;++i)
{
LL w=M/m[i];
exgcd(m[i],w,d,d,y);
x=(x+y*w*a[i])%M;
}
return (x+M)%M;
}
为了简单起见,我们只考虑一种最简单的情况,即当n为素数时,解模方程
\[
a^x\equiv b(mod\quad n)
\]
因为n是素数,只要a不为0,一定存在逆a^-1。根据欧拉定理,只需检查x=0,1,2,...,n-1是不是解即可。因为
\[
a^{n-1}\equiv 1(mod\quad n)
\]
,当x超过n-1时a^x就开始循环了。
我们先检查前m(我们使m为n^(1/2))项,即a^0,a^1,...,a^(m-1)模n的值是否为b,并把a^imodn保存在ei中,并求出a^m的逆a^(-m)。
下面考虑a^m,a^(m+1),...,a^(2m-1)。这次不用一一检查,因为如果它们中有解,则相当于存在i使得
\[
e_i*a^m\equiv b(mod\quad n)
\]
,两边同乘a^(-m)得
\[
e_i\equiv b'(mod\quad n)
\]
其中
\[
b'= a^{-m}b(mod\quad n)
\]
这样只需检验,是否有这样的ei等于b‘即可。
若没有,再考虑a^2m,a^(2m+1),...,a^(3m-1),故b‘‘=(a^(-2m)*b)mod n
,所以我们一直要按照这样的方法枚举到a^(m*m-1)
int pow_mod(int a,int b,int mod)
{
int ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
int log_mod(int a,int b,int n)
{
int m,v,e=1;
m=(int)sqrt(n+0.5);
v=pow_mod(pow_mod(a,m,n),n-2,n);
map<int,int> x;
x[1]=0;
for(int i=1;i<m;++i)
{
e=(e*a)%n;
if(!x.count(e)) x[e]=i;
}
for(int i=0;i<m;++i)//考虑a^(im),a^(im+1),...,a^(im+m-1)
{
if(x.count(b)) return i*m+x[b];
b=(b*v)%n;
}
return -1;
}//这就是可用于解决离散对数的大步小步算法(Baby_Step_Giant_Step Algorithm),复杂
//度O(n^(1/2)logn)
标签:这一 意义 isp 自己 step prim log 因此 str
原文地址:https://www.cnblogs.com/iwillenter-top1/p/11616178.html