数论基础一(快速幂,质数,欧拉函数)
数学&数论
 
# 01 快速幂
一种快速求解幂的方法
(貌似是一句废话)
思路是使用二进制求解 
example

 11    (1011)Bin   (1000)Bin   (10)Bin   (1)Bin
3   = 3          =3         * 3        *3

         			    3         1        0
     				  2         2        2
   				 =  3     +   3     +  3
(蒟蒻作者不会 Latex ,凑合一下吧)
而明显我们可以快速求出 x^(2^k) 的值

another example
-----------------------------------------------------
编号  |  res  |  级数 |  每次执行平方操作后 res 的值 
  1   |   3   |    1  |           9
  2   |   9   |    2  |           81
  3   |   81  |    4  |           81*81
 ...  |  ...  |   2^k |           ...
-----------------------------------------------------
  
可见只需通过平方就可以求出 x^(2^k) 
然后搞一些简简单单的位运算就可以啦
注意快速幂一般都需要取模
(要不然 C++ 的 pow 他不香吗)
 
Code [P1226 快速幂]
#include<bits/stdc++.h>
using namespace std;
long long p;
long long Qpow(long long base,long long add){
	long long cnt=1;
	while(add){
		if(add&1)cnt=(cnt*base)%p;
		base=(base*base)%p;
		add>>=1;
	}
	return cnt;
}
int main(){
	long long a,b; 
	scanf("%lld%lld%lld",&a,&b,&p);
	printf("%lld^%lld mod %lld=%lld",a,b,p,Qpow(a,b));
	return 0;
}


# 02 质数

	## 01 判断质数
	判断质数一般用试除法
	最暴力的方法显然是从 2 到 n-1 逐个遍历检查
	根据小学知识一个数的因数一般成对存在
	所以只需遍历到 sqrt(x) 即可
	//只需判断 i < x/i 即可 
	
	Code 
	bool IsPrime(int key){
		for(int i=2;i<=key/i;i++)if(key%i==0)return false;
		return true;
	}

	## 02 筛质数
		### 01 埃氏筛 
		埃氏筛不讲了,上代码
		
		Code [埃氏筛]
		int prime[N],cnt=0;//记录质数的值和数量 
		bool st[N];//标记数组 为 0 表示是质数 
		void Ai_Shi_Shai(int lim){
			st[1]=true;
			for(int i=2;i<=lim;i++){
				if(st[i]==false){
					prime[++cnt]=i;
					for(int j=i+i;j<=n;j++)st[i]=true;
				}
			}
		}
		
		### 02 线性筛
		顾名思义时间复杂度 O(n)
		思路是只被其最小质因子筛去
		我们用每一个数和所有质数相乘进行筛除
		一开始能保证 prime[j] 是乘积的最小质因子
		直到 i 的最小质因子等于 prime[j] 为止
		此时直接 break 就好
		 
		Code [P3383 线性筛质数模板]
		#include<bits/stdc++.h>
		using namespace std;
		const int N=1e8+9,M=1e7+9;
		int prime[M],cnt;
		bool st[N];
		void Eular(int n){
			st[1]=true;
			for(int i=2;i<=n;i++){
				if(!st[i])prime[++cnt]=i;
				for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
					st[i*prime[j]]=1;
					if(i%prime[j]==0)break;
				}
			}
		}
		int main(){
			int n,T,p;
			scanf("%d%d",&n,&T);
			Eular(n);
			while(T--){
				scanf("%d",&p);
				printf("%d\n",prime[p]);
			}
			return 0;
		}
	
# 03 约数,ex欧拉筛与欧拉函数 

	# 00 前置知识:积性函数
	如果一个函数 Shit!(x) 满足
	Shit!(a) * Shit!(b) = Shit!(a*b)  (GCD(a,b)=1,即 a,b 互质)
	则称这个函数 Shit!(x) 为积性函数 
	
	# 01 欧拉函数
	欧拉函数 phi(n) 指小于等于 n 且与 n 互质的数的个数 
	欧拉函数性质:
		1.  phi(1) =1
		->	因为 1 和 1 互质
		2.  phi(prime)=prime-1
		->	明显从 1 ~ prime 中除 prime 本身任何数都与 prime 互质
		3.  phi(prime^k)=prime^k-prime^(k-1) 
		-> 	可知 1 ~ prime^k 中 只要不是 prime 的倍数则都与原数互质
			而在此范围内 prime 的倍数有 (prime^k)/(prime)=prime^(k-1) 个
			所以用 prime^k 减去该数即为结果
		4.  phi(x) 是积性函数
		->	这个证明过程较为复杂,就不贴了
			(没事,写了你就不会在意证明了)

				      p[1]		p[2]                  p[k]
		5.  若 N= a[1]    * a[2]		 * ... *  a[k]
            (a[i] 为质数,p[i] 为正整数)
            则 phi(N)=N(1-1/a[1])(1-1/a[2])...(1-1/a[k])
            //实际上是 phi 函数的解析式
        ->  使用了性质三和四
            由性质四
            phi(N)=phi(a[1]^p[1])*phi(a[2]^p[2])* ... *phi(a[k]*p[k])
            然后把性质三带入
            phi(N)=(a[1]^p[1]-a[1]*(p[1]-1))*(a[2]^p[2]-a[2]^(p[2]-1))* ... *(a[k]^p[k]-a[k]^(p[k]-1))
                  =a[1]^p[1](1-1/a[1])* ... *a[k]^p[k](1-1/a[k])
                  =(a[1]^p[1])*(a[2]^p[2])* ... *(a[k]^p[k])*(1-1/a[1])*(1-1/a[2])* ...  *(1-1/a[k]) 
            最终将 N 的唯一分解定理代入
            phi(N)=N(1-1/a[1])* ... *(1-1/a[k])
            QED!
    然而如果要求出 1~n 中每一个数的欧拉函数
    (那阁下又将如何应对呢)(我再也不玩抽象了)
    我们发现这个东西欧拉筛可以干
    当然根据欧拉筛的思路
    我们主要使用已有的 phi 去得到其他数的 phi 值
    分成三种情况
        1.  k 是质数
        ->  phi(k)=k-1
        2.  GCD(k,prime) = 1
        ->  由积性函数性质
            phi(k*prime)=phi(k)*phi(prime)=(prime-1)*phi(k)
        3.  GCD(k,prime) = prime
        ->  说明 k 已有 prime 这个因数
            看上面的唯一分解定理只有其中的 N 会有所改变
            所以 phi(k*prime)=phi(k)*prime
    
    Code [ACP10071 筛法求欧拉函数]
	#include<bits/stdc++.h>
	using namespace std;
	const int N=1e6+7;
	int prime[N],phi[N],cnt;
	bool st[N];
	void Eular_Get_Phi(int Lim){
	    st[1]=1,phi[1]=1;
	    for(int i=2;i<=Lim;i++){
	        if(st[i]==0){
	            phi[i]=i-1;
	            prime[++cnt]=i;
	        }
	        for(int j=1;j<=cnt&&(long long)prime[j]*i<=(long long)Lim;j++){
				st[i*prime[j]]=true;
	            if(i%prime[j]==0){
	                phi[i*prime[j]]=prime[j]*phi[i];
	                break;
	            }else{
	                phi[i*prime[j]]=(prime[j]-1)*phi[i];
	            }
	        }
	    }
	
	}
	int main(){
	    int n;
	    scanf("%d",&n);
	    Eular_Get_Phi(n);
	    long long ans=0;
	    for(int i=1;i<=n;i++)ans+=phi[i];
	    printf("%lld\n",ans);
	    return 0;
	}
	
	# 02 约数个数函数
    用 d(x) 表示
	顾名思义这个函数指的是 x 的约数的个数
	解析式如下:
	  		  p[1]		 p[2]              p[k]
	若 N= a[1]    * a[2]		 * ... *  a[k]  
	则 d(N)=(p[1]+1)*(p[2]+1)* ... *(p[k]+1)
	这个函数其实比较简单
	显然每个质因数 a[i] 都可以选择 0~k[i] 的次数
	乘法原理即可得出原式
	(以下正片开始)
	显然该函数为积性函数
	(这就不需要玄学了,如果 a,b 两个数互质则他们的乘积在唯一分解定理中不会有重叠,稍加思考即可得出)
	发现一件事情
	很多积性函数都可以用欧拉筛求解
	仍然分成三种情况
        1.  k 是质数
        ->  D(k)=2
        2.  GCD(k,prime) = 1
        ->  由积性函数性质
            D(k*prime)=D(k)*D(prime)=2*D(k)
        3.  GCD(k,prime) = prime
        ->  说明 prime 是 k 的最小质因子 a[i]
            显然 D(k*prime)=(p[1]+1)*(p[2]+1)* ... *(p[i]+1+1)* ... *(p[k]+1)
    所以我们还需要维护每个数最小质因子的次数 MinRoot
    仍然分成三种情况
    	1.  k 是质数
        ->  MinRoot(k)=1
        2.  GCD(k,prime) = 1
        ->  prime 是 k 唯一的最小质因子 a[i]
            MinRoot(k*prime)=1
        3.  GCD(k,prime) = prime
        ->  说明 prime 是 k 的最小质因子 a[i]
        	所以会将此次数 +1
        	即 MinRoot(k*prime)=MinRoot(k)+1
    QED!
    Code [P1403 约数研究]
	//好冷门的题,还是道橙题
	//是,我不是正解
	//可是你能找到例题吗
	//拉倒吧我园子没油了
	#include<bits/stdc++.h>
	using namespace std;
	const int N=1e6+7;
	bool st[N];
	int prime[N],cnt,MinRoot[N],D[N];
	void Eular(int Lim){
		st[1]=1,D[1]=1;
		for(int i=2;i<=Lim;i++){
			if(!st[i]){
				prime[++cnt]=i;
				MinRoot[i]=1;
				D[i]=2;
			}
			for(int j=1;j<=cnt&&(long long)i*prime[j]<=(long long)Lim;j++){
				st[i*prime[j]]=true;
				if(i%prime[j]==0){
					MinRoot[i*prime[j]]=MinRoot[i]+1;
					D[i*prime[j]]=D[i]/(MinRoot[i]+1)*(MinRoot[i]+2);
					break;
				}else{
					MinRoot[i*prime[j]]=1;
					D[i*prime[j]]=2*D[i];
				}
			}
		}
	}
	int main(){
		int n;
		scanf("%d",&n);
		Eular(n);
		long long ans=0;
		for(int i=1;i<=n;i++)ans+=D[i];
		printf("%lld\n",ans);
		return 0;
	}    
posted on 2025-01-18 11:42  2025ing  阅读(14)  评论(2编辑  收藏  举报