算法竞赛模板(数论)
Posted leimingzeOuO
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法竞赛模板(数论)相关的知识,希望对你有一定的参考价值。
目录
质数
1e11以内质数个数
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 5e6 + 2;
bool np[N];
int prime[N], pi[N];
int getprime()
int cnt = 0;
np[0] = np[1] = true;
pi[0] = pi[1] = 0;
for(int i = 2; i < N; ++i)
if(!np[i]) prime[++cnt] = i;
pi[i] = cnt;
for(int j = 1; j <= cnt && i * prime[j] < N; ++j)
np[i * prime[j]] = true;
if(i % prime[j] == 0) break;
return cnt;
const int M = 7;
const int PM = 2 * 3 * 5 * 7 * 11 * 13 * 17;
int phi[PM + 1][M + 1], sz[M + 1];
void init()
getprime();
sz[0] = 1;
for(int i = 0; i <= PM; ++i) phi[i][0] = i;
for(int i = 1; i <= M; ++i)
sz[i] = prime[i] * sz[i - 1];
for(int j = 1; j <= PM; ++j) phi[j][i] = phi[j][i - 1] - phi[j / prime[i]][i - 1];
int sqrt2(LL x)
LL r = (LL)sqrt(x - 0.1);
while(r * r <= x) ++r;
return int(r - 1);
int sqrt3(LL x)
LL r = (LL)cbrt(x - 0.1);
while(r * r * r <= x) ++r;
return int(r - 1);
LL getphi(LL x, int s)
if(s == 0) return x;
if(s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s];
if(x <= prime[s]*prime[s]) return pi[x] - s + 1;
if(x <= prime[s]*prime[s]*prime[s] && x < N)
int s2x = pi[sqrt2(x)];
LL ans = pi[x] - (s2x + s - 2) * (s2x - s + 1) / 2;
for(int i = s + 1; i <= s2x; ++i) ans += pi[x / prime[i]];
return ans;
return getphi(x, s - 1) - getphi(x / prime[s], s - 1);
LL getpi(LL x)
if(x < N) return pi[x];
LL ans = getphi(x, pi[sqrt3(x)]) + pi[sqrt3(x)] - 1;
for(int i = pi[sqrt3(x)] + 1, ed = pi[sqrt2(x)]; i <= ed; ++i) ans -= getpi(x / prime[i]) - i + 1;
return ans;
LL lehmer_pi(LL x)
if(x < N) return pi[x];
int a = (int)lehmer_pi(sqrt2(sqrt2(x)));
int b = (int)lehmer_pi(sqrt2(x));
int c = (int)lehmer_pi(sqrt3(x));
LL sum = getphi(x, a) +(LL)(b + a - 2) * (b - a + 1) / 2;
for (int i = a + 1; i <= b; i++)
LL w = x / prime[i];
sum -= lehmer_pi(w);
if (i > c) continue;
LL lim = lehmer_pi(sqrt2(w));
for (int j = i; j <= lim; j++) sum -= lehmer_pi(w / prime[j]) - (j - 1);
return sum;
int main()
init();
LL n;
cin>>n;
cout<<lehmer_pi(n)<<endl;//输出n以内有多少个质数,可以输出1e11以内的
return 0;
快速求出n!的质因子个数
for(int i=0;i<cnt;i++)
LL p=primes[i],a=p;
LL s=0;
while(p<=n)s+=n/p,p*=a;
cout<<a<<' '<<s<<endl;
线性筛质数
void get_primes(int n)
//外层从2~n迭代,因为这毕竟算的是1~n中质数的个数,而不是某个数是不是质数的判定
for(int i=2;i<=n;i++)
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)//primes[j]<=n/i:变形一下得到——primes[j]*i<=n,把大于n的合数都筛了就
//没啥意义了
st[primes[j]*i]=true;//用最小质因子去筛合数
//1)当i%primes[j]!=0时,说明此时遍历到的primes[j]不是i的质因子,那么只可能是此时的primes[j]<i的
//最小质因子,所以primes[j]*i的最小质因子就是primes[j];
//2)当有i%primes[j]==0时,说明i的最小质因子是primes[j],因此primes[j]*i的最小质因子也就应该是
//prime[j],之后接着用st[primes[j+1]*i]=true去筛合数时,就不是用最小质因子去更新了,因为i有最小
//质因子primes[j]<primes[j+1],此时的primes[j+1]不是primes[j+1]*i的最小质因子,此时就应该
//退出循环,避免之后重复进行筛选。
if(i%primes[j]==0) break;
分解质因数
void divide(int n)
for(int i=2;i<=n/i;i++)
if(n%i==0)
//n不包含任何从2到i-1之间的质因子(已经被除干净了)
//(n%i==0)所以i也不包含何从2到i-1之间的质因子,由质数的定义可知,保证了i是质数
int s=0;
while(n%i==0) n/=i,s++;
cout<<i<<' '<<s<<endl;
if(n>1) cout<<n<<' '<<1<<endl; //最多只有一个大于根下n的质因子(两个相乘就大于n了)
cout<<endl;
分解质因数优化(线性筛+分解质因数)
将枚举i
直接变为枚举质因子,这里用求约数个数举例
for(int i=0;i<cnt&&primes[i]*primes[i]<=x;i++)
if(x%primes[i]==0)
int s=0;
while(x%primes[i]==0)
s++;
x/=primes[i];
res=res*(s+1);
if(x>1)res=res*2;
欧拉函数
常用小结论:
- 质数的欧拉函数等于它本身-1
求某个数的欧拉函数
int phi(int x)
//公式:num=nnum*(1-1/p_1)*(1-1/p_2)....*(1-1/p_i);
int res=x;
for (int i=2;i<=x/i;i++)
if (x%i==0)//分解质因数
while (x%i==0)x/=i;
res=res/i*(i-1);//先除再乘防止爆long long
if (x>1)res=res/x*(x-1);
return res;
线性筛欧拉函数
int n;
const int N=1e6+10;
int primes[N],phi[N],cnt;//存N个质数,1~N每个数的欧拉函数
bool st[N];//质数false,合数true
int get_eulers(int n)
phi[1]=1; //1的欧拉函数只有一个,如果求小于它本身的与它互质的个数,注释此
for(int i=2;i<=n;i++)
if(!st[i])
//如果没有被筛掉
primes[cnt++]=i;
phi[i]=i-1; //说明这个数是质数,而质数的欧拉函数为i - 1个
for(int j=0;primes[j]<=n/i;j ++)
int t=primes[j]*i;
st[t]=true;
if(i%primes[j]==0)
//这边上面推导过
phi[t]=phi[i]*primes[j];
break;
phi[t]=phi[i]*(primes[j]-1);
int res=0;
for(int i=1;i<=n;i ++)res+=phi[i]; //把所有数的欧拉函数加起来,也就是答案
return res;
约数
约数个数
map<int,int>mp;//i的指数
int get(int x)
for(int i=2;i<=x/i;i++)
if(x%i==0)
while(x%i==0)
x/=i;
mp[i]++;
if(x>1)mp[x]++;
int res=1;
for(auto x:mp)res=res*(x.y+1);
return res;
约数个数(优化版本)
for(int i=0;i<cnt&&primes[i]*primes[i]<=x;i++)
if(x%primes[i]==0)
int s=0;
while(x%primes[i]==0)
s++;
x/=primes[i];
res=res*(s+1);
if(x>1)res=res*2;
约数求和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int t;
int n;
unordered_map<int,int>mp;
void get(int n)
for(int i=2;i<=n/i;i++)
if(n%i==0)
while(n%i==0)
mp[i]++;
n/=i;
if(n>1)mp[n]++;
int main()
cin>>t;
while(t--)
cin>>n;
get(n);
LL res=1;
for(auto x:mp)
int a=x.first,b=x.second;
LL t=1;
while(b--)t=(t*a+1)%mod;
res=(res*t)%mod;
cout<<res<<endl;
return 0;
约数之和(优化版本)
int n;
const int N=5e4+10,mod=1e9+7;
int以上是关于算法竞赛模板(数论)的主要内容,如果未能解决你的问题,请参考以下文章
我的所有优质博客全部开源啦(我自己原创的《ACM模板》《算法全家桶》《算法竞赛中的初等数论》 PDF免费下载)
《算法竞赛中的初等数论》正文 0x50筛法(ACM / OI / MO)(十五万字符数论书)
《算法竞赛中的初等数论》正文 0x50筛法(ACM / OI / MO)(十五万字符数论书)