noip复习之数学(3)——数论
1.基本概念和常用代码
(1)素数(质数)
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;//每个数都只会被它最小的素数因子筛掉
}
}
}//线性筛素数
(2)欧几里得算法(辗转相除法)
#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即可)
扩展欧几里得可以求逆元(逆元或许可以理解为在模意义下的倒数)
若要求在模意义下的逆元,首先在模意义下有逆元的前提条件是,即和互质,我们假设在模意义下的逆元为,则在模时同余于,即可得(应是,移项后就变为了,我们令,则)
(3)快速幂
#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;
}
}
(4)欧拉函数(不超过自己并与自己互质的数的个数)
证明:
给出正整数n的唯一分解式
用容斥原理,首先从总数中减去的倍数的个数(都是素数,故不是其倍数即为与其互质),即(设最大的满足小于等于的倍数为,故求得为$\frac{n}{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);
}
}
}
}//筛表法求欧拉函数
(5)剩余系,模乘法的逆
通俗的说,模n的完全剩余系就是,而简化剩余系(也称缩系)就是完全剩余系中与互素的那些数。
模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是素数,则
所以的逆就是_
2.模方程
(1)线性模方程
把它转化为,当不是的约数时无解,否则两边同时除以,得到,即为
此时和已经互素,因此再左乘在模意义下的逆元,则解为:
这个解是模n'剩余系中的一个元素,但我们还需要把它表示为模n剩余系中的元素。令,上述解相当于,...。对于模n来说,假设和同余,则是的倍数,因此,是的倍数。换句话说,在模剩余系下,
恰好有个解,为。
如果有多个方程,变量还是只有一个,又该怎么做呢?
那就用到了下面的定理
(2)中国剩余定理(Chinese Remainder Theorem)
假设有方程组
且所有的mi两两互素。令为所有的乘积,,则。
用扩展欧几里得算法可以找到和使得。然后令,则方程组等价于单个方程
,即在模剩余系下,原方程组有唯一解。
证明:把等式两边模后立即可得
,而对于所有不等于的,是的倍数,因此
,这样,对取模时,除了这一项余数为之外,其余项的余数均为。
#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;
}
(3)离散对数
为了简单起见,我们只考虑一种最简单的情况,即当为素数时,解模方程
因为是素数,只要不为,一定存在逆。根据欧拉定理,只需检查是不是解即可。因为
,当超过时就开始循环了。
我们先检查前(我们使为)项,即模n的值是否为,并把保存在中,并求出的逆。
下面考虑。这次不用一一检查,因为如果它们中有解,则相当于存在i使得
,两边同乘得
其中
这样只需检验,是否有这样的等于即可。
若没有,再考虑,故
,所以我们一直要按照这样的方法枚举到
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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现