算法竞赛模板(数论)

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. 质数的欧拉函数等于它本身-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)(十五万字符数论书)

《算法竞赛中的初等数论》正文 0x60 原根(ACM / OI / MO)(二十万字符数论书)

《算法竞赛中的初等数论》正文 0x50筛法(ACM / OI / MO)(十五万字符数论书)