数论及模板
文章首发于:My Blog 欢迎大佬们前来逛逛
求两个正整数的最大公约数
时间复杂度:O(NlogN)
int gcd(int a,int b){ return b?gcd(b,a%b):a; }
线氏筛求质数
时间复杂度:O(N)
int nums[N]; void get_primes(int n){ bool vis[N]; int cnt=0; for (int i=2;i<=n;i++){ if (!vis[i]){ nums[cnt++]=i; } for (int j=0;nums[j]*i<=n;j++){ vis[nums[j]*i]=true; if (i%nums[j]==0){ break; } } } //nums; }
196. 质数距离
求出在【L,R】区间内某两个质数分别是距离最小的和距离最大的。
性质一 : 数字N如果是一个合数,则N一定存在一个小于等于sqrt(N)的质因子。
假设a和b是N的两个质因子,则a*b=N,假设 a<=b,则 a * a <= a * b = N,则可以得出:a<=sqrt(N)
性质二: 如果x属于【L,R】的区间中合数,则一定存在一个p值,满足p<=sqrt(N),并且这个p值 是x的一个质因子,则便可以通过 p 值来筛选出在【L,R】区间中的每一个合数。
步骤:
预处理质数
:首先由于数据都是小于 2^31-1 的,因此最大的质因子为 sqrt(2^31 -1 ),粗略计算大概是50000,则我们只需要预处理出[1,50000] 的质数即可。筛出合数
:然后根据 [1,50000]之间的质数 p,通过遍历p的倍数,来筛出 [L,R]区间中所有的合数,则剩下的就一定全部都是质数。寻找距离
:然后在剩下的质数数组中寻找相邻的两个数字的距离最小和最大的。
细节:【x】表示上取整x
- 在筛出合数的过程中,我们需要找到[L,R]区间中所有的合数,因此对于每一个枚举的p,都需要首先找到大于等于L的第一个初始的位置,然后在
i<=r
的条件下每次+=p
,这样这些位置标记的都是合数。 - 对于这个初始的位置,我们应该取得[L,R]的第一个合数,具体的做法为:
- 如果初始的p不在[L,R]的区间内:初始的位置应该是
【L/p】*p
. - 如果初始的p在[L,R]的区间内:初始的位置至少为
2*p
,因为p是需要循环的第一个质数,2*p是第一个合数
- 如果初始的p不在[L,R]的区间内:初始的位置应该是
如何表示 2 过程的上取整呢?公式如下:
$$
\left \lceil \frac{L}{P} \right \rceil\times P => (\frac{L-1}{P}+1) \times P
$$
因此对于上面的描述过程,我们可以这样描述:
for (int i=1;i<=cnt;i++){ int p=nums[i];//取得这个初始的p值 for (int j=max((L-1)/p+1)*p,2*p);j<=r;j+=p){//在[L,R]内寻找p的倍数,他们都是合数 //标记合数 } }
完整代码如下:
#include <iostream> #include <cstring> #include <algorithm> #include <queue> #include <vector> #include <cmath> #include <limits.h> using namespace std; const int N = 1e6+10; #define int long long int l,r; int nums[N],cnt; bool temps[N]; //线氏筛求质数: O(N) void get_num(int n){ memset(temps,0,sizeof(temps)); for (int i=2;i<=n;i++){ if (!temps[i]){ nums[cnt++]=i; } for (int j=0;nums[j]*i<=n;j++){ temps[nums[j]*i]=true; if (i%nums[j]==0){ break; } } } } signed main() { //预处理质数 while (cin>>l>>r){ get_num(50000); /* 如果N是一个合数,则N一定存在一个小于等于sqrt(N)的质数: 假设a和b是N的两个质数,并且假设a<=b,则 a*a <= a*b = N, 则 a<=sqrt(N),a就是N的一个小于等于sqrt(N)的质数 */ //1. 首先筛出[1,50000]之间所有的素数 //2. 把 [L,R]范围内的所有的合数“删除”掉,则剩下的就是目标范围内的质数 memset(temps,0,sizeof(temps)); for (int i=0;i<cnt;i++){ int p=nums[i];//取得每一个[1,50000]之间的质数,对这个数字取倍数,则生成的数字一定是合数 for (int j=max((l-1)/p+1)*p,2*p);j<=r;j+=p){ temps[j-l]=true; //相当于离散化,防止数组下标过大,减去左边界 } } cnt=0; for (int i=0;i<=r-l;i++){ if (!temps[i] && i+l>1){ nums[cnt++]=i+l;//再加上l,就变成了原数 } } if (cnt<2){ cout<<"There are no adjacent primes.\n"; } else{ //获得答案 int minp=0,maxp=0; for (int i = 0; i+1 < cnt; i ++ ){ int d=nums[i+1]-nums[i]; if (d<nums[minp+1]-nums[minp]){ minp=i; } if (d>nums[maxp+1]-nums[maxp]){ maxp=i; } } printf("%d,%d are closest, %d,%d are most distant.\n",nums[minp],nums[minp+1],nums[maxp],nums[maxp+1]); } } return 0; }
求组合数
求组合数:C(n,k)
,即在 n个数字中选择k个数字。
int C[N][N]; void get(){ for (int i=0;i<=n;i++){ for (int j=0;j<=i && j<=k;j++){ //小于等于k为一个小优化 if (!j) C[i][j]=1; //j为零的时候初始化,即C(i,0)=1 else C[i][j]=C[i-1][j-1]+C[i-1][j]; //类似于杨辉三角 } } }
通过这种方式得到C(n,k)的值:C[n][k]
时间复杂度:O(N^2)
快速幂
计算X ^ N % mod
的值
模板代码如下:
#define int long long int x,n,mod=9999999; int get(){ int ans=1;//初始化为1 for (;n;n>>=1){ if (n&1){ ans=ans*x%mod; } x=x*x%mod; } return ans; }
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17289296.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)