数论知识总结
数论知识总结
介绍
1.素数
定义:对于一个正整数,如果它有且仅有 和它自己两个约数,那么我们称这个数为素数。如果有两个以上的约数,那么我们称这个数为合数。
唯一因子分解定理(质因数分解):合数能被唯一的分解为质数的乘积形式:
。
如何求素数
- 暴力枚举:。
- 筛法求素数:用每个素数去筛掉它的倍数,时间 。
- 线性筛素数:在筛法的基础上 我们让每一个合数只能被他自己最小的素数筛到。
直接看代码:
for(int i = 2;i <= n;i++)
{
if(!isp[i])p[++cnt] = i;
for(int j = 1;j <= cnt&&i*p[j] <= n;j++)
{
isp[i*p[j]] = true;
if(i%p[j]==0) break;
}
}
2.gcd相关
定义: 表示 和 的最大公约数, 表示 和 的最小公倍数。
求法
知道这两个公式就行了:,。
代码:
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int lcm(int x,int y){return x/gcd(x,y)*y;}
裴蜀定理
P4549 【模板】裴蜀定理
裴蜀定理:设 ,则d为 ()中的最小正数( 的倍数均在集合中且集合中的数都是 的倍数)
一个重要的推论是: 有解等价于 和 互质。
扩展欧几里得
欧几里得算法只能告诉我们 这个方程有没有解( 成不成立),而不能说明解是多少,使用扩展欧几里得算法可以解决这个问题。
欧几里得算法是一个递归的过程,那我们也用递归来求解 和 ,现在已知 和 ,求 和 。
变个形:
然后把含 的和含 的分别凑一块: 就是递推关系了。
代码:
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x = 1;y = 0;return;}
exgcd(b,a%b,x,y);ll t = x;
x = y;y = t-a/b*y;
}
逆元
对于一个数 ,它在模 意义下的逆元 ,满足 。
如何求逆元?
- 扩展欧几里得,这式子不是和 长得很像吗?直接做就完了,时间复杂度:。
- 费马小定理:若 为素数,则 ,所以 就是 的逆元,用快速幂就行了。
- 线性求逆元:
将 写成 的形式,其中 (即带余除法)。
两边同时除以 得:
所以
代码就是:
inv[0] = inv[1] = 1;
for(int i = 2;i <= n;i++)inv[i] = inv[mod%i]*(mod-mod/i)%mod;
P3811 【模板】乘法逆元
P2613 【模板】有理数取余
中国剩余定理(CRT)
P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪
和拉格朗日插值有点像,只需要构造出一个 就行了。
设 ,, 为 的逆元,则 ,那么最小解就是 。
证明很简单,你把每个式子带进去算一下就行了。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int n,a[11],b[11];
ull sum=1,t[11],ans;
int main()
{
cin>>n;
for(int i=1; i<=n; i++) {
cin>>a[i]>>b[i];
sum*=a[i];
}
for(int i=1; i<=n; i++) t[i]=sum/a[i];
for(int i=1; i<=n; i++)
for(ull j=t[i]; j<=0x7f7f7f7f7f7f; j+=t[i])
if(j%a[i]==1) {ans+=j*b[i]%sum;break;}
cout<<ans%sum;
return 0;
}
扩展中国剩余定理(exCRT)
P4777 【模板】扩展中国剩余定理(EXCRT)
和普通 一样有一组同于方程组,只不过模数不一定是质数。
思路也不一样了,是两个同余方程先合并,得到一个 的解,再依次合并下去。
代码:
#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int N = 1e5+5;
int n;
ll s[N],yu[N],ans;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x = 1;y = 0;return a;}
ll g = exgcd(b,a%b,x,y),tp = x;
x = y;y = tp-a/b*y;
return g;
}
ll CRT()
{
ll yu1 = yu[1],s1 = s[1];
for(int i = 2;i <= n;i++)
{
ll yu2 = yu[i],s2 = s[i];
ll d = yu2-yu1,g,x,y;
g = exgcd(s1,s2,x,y);
if(d%g)return -1;
ll now = s2/g;
x = (x*(d/g)%now+now)%now;
yu1 = x*s1+yu1;s1 = s1/g*s2;
}
return yu1;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
scanf("%lld%lld",s+i,yu+i);
printf("%lld\n",CRT());
return 0;
}
3.组合数学
定义:
从 个互不相同的物品里面选出 个排成一列(有先后顺序),总共有多少种选法?
这是一个排列问题,我们将这个方案数定义为 。
第一次有n种取法,第二次有 种…第 次有 种,根据乘法原理,方案数应该为 。即 。
那如果没有先后顺序呢?这个方案数就是 ,也可以记作 。
可以发现组合数相对排列数,就只是把顺序去掉了,即 和 是同一种选法,把 个无序的物品排成有序的有 种方法,所以 。
关于组合数的性质:
- ,不选也是一种方案。
- 。
- ,从 个里面拿出 个和留下 个等价。
- ,可以由这条性质 求出杨辉三角。
- 可以根据第 条性质推出:。
二项式定理
即求 拆开后各项的系数。
可以单独考虑 ,就是从 个 中选出 个 ,即 。
所以 。
应用:
即奇数项之和等于偶数项之和。
即所有项之和为 。
你可以将上面两式相减(加)再除以 ,就可以求得奇数项(偶数项)之和。
LUCAS
如果要求 ,但 太大时,就可以用 LUCAS。
在 或 时只要反复对第一部分使用该定理即可
使用条件: 是较小质数
P3807 【模板】卢卡斯定理/Lucas 定理
ll inv[N],fac[N];
int lucas(int n,int m,int p)
{
if(n < m)return 0;
if(n < p)return fac[n]*inv[m]*inv[n-m]%p;
return lucas(n/p,m/p,p)*lucas(n%p,m%p,p)%p;
}
int main()
{
int T;cin >> T;
while(T--)
{
int n,m,p;scanf("%d%d%d",&n,&m,&p);n += m;
fac[0] = inv[0] = inv[1] = 1;
for(int i = 1;i <= n;i++)fac[i]=fac[i-1]*i%p;
for(int i = 2;i <= n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
for(int i = 1;i <= n;i++)inv[i]=inv[i-1]*inv[i]%p;
printf("%d\n",lucas(n,m,p));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现