从入门到入土———数论

目录

第一章 勾股数组

本原勾股数组

本原勾股数组是一个三元组(a,b,c,),其中a,b,c没有公因数,且满足a2+b2=c2

勾股数组定理

每个本原勾股数组(a,b,c)其中(a为奇数,b为偶数)都可以从以下公式得出:a=st,b=(s2-t2)/2,c=(s2+t2)/2,其中s > > t \geq 1。

定理一

a和b奇偶不同,且c为奇数。
证明:
假设a和b都是偶数,则c一定为偶数,于是a,b,c存在最小公因式2,不满足本原勾股数a,b,c没有公因式的条件,假设不成立。
假设a,b都为奇数,则c一定为偶数,于是设a=2x+1,b=2y+1,c=2z,∵a2+b2=c2 \Longrightarrow 4x2+4x+1+4y2+4y+1=4z2 \Longrightarrow 2x2+2x+2y2+2y+1=2z2,而奇数不可能等于偶数,假设不成立。
故a,b奇偶不同,且c一定为奇数。

定理二

a为奇数,b为偶数,则c+b和c-b都是完全平方数。
证明:
设正整数d为c+b,c-b的公因数,则d整除c-b和c+b,d也整除(c+b)+(c-b)=2c和(c+b)-(c-b)=2b,由于b,c互素,因此d等于1或2,但d整除
(c-b)(c+b)=a2,由于a为奇数,所以d不能等于2,因此d等于1,因此c+b和c-b无公因数,由于(c-b)(c+b)=a2,a2为平方数,且(c+b)与(c-b)互素,因此二者均为平方数。

推论

由定理二,不妨设c-b=t2,c+b=s2,则可以解出a=st,b= s 2 t 2 2 \frac{s^{2}-t^{2}}{2} ,c= s 2 + t 2 2 \frac{s^{2}+t^{2}}{2} ,其中s > > t \geq 1。。
若优势题目给出a,可令t=1,s=a,则可直接推出b和c。

第二章 勾股数组与单位圆

由上一张勾股数组a2+b2=c2,两边同时除以c2,可得(a/c)2+(b/c)2=1,其形式上近似与单位圆的方程,x2+y2=1,据勾股数组定理,可以得出任何圆上的可行点都可以表示为x= 2 s t x 2 + t 2 \frac{2st}{x^{2}+t^{2}} ,y= s 2 t 2 s 2 + t 2 \frac{s^{2}-t^{2}}{s^{2}+t^{2}} ,令m=s/t,,由于x和y的实际含义被扩展到了实数域上,因此m的范围也就变成了
(-∞,∞),圆x2+y2=1上坐标是有理数的店都可以由公式
(x,y)= 1 m 2 1 + m 2 \frac{1-m^{2}}{1+m^{2}} , 2 m 1 + m 2 \frac{2m}{1+m^{2}} )表示,其中m为有理数((-1,0)除外,此为m趋于∞是的极限值)。

第三章 高次幂之和与费马大定理

费马大定理:n \ge 3时,方程xn+yn=zn没有正整数解。

第四章 整除性与最大公因式

假设m与n是整数,且m不等于0,m整除n是指n是m的倍数,即存在整数k是的n=mk,记作m|n。

欧几里得算法

通过递推求a与b的最大公因数,记为gcd(a,b)。

typedef long long ll;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}

第五章 线性方程和最大公因数

裴蜀定理

对于a,b,存在x,y使得ax+by=gcd(a,b)。

扩展欧几里得

求解gcd的同时求解方程ax+by=gcd(a,b)的一个解( x 0 x_{0} y 0 y_{0}) ,复杂度O(log(b))。

ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
y=0;
x=1;
return a;
}
ll r=exgcd(b,a%b,x,y);
ll temp=y;
y=x-(a/b)*y;
x=temp;
return r;
}

线性方程定理

设a与b是非0的整数,ax+by=gcd(a,b),总有一个解( x 0 x_{0} y 0 y_{0} )且这个解可以通过扩展欧几里得算法得到,且方程的每一个解就可以表示为( x 0 x_{0} +k b g c d ( a , b ) \frac{b}{gcd(a,b)} y 0 y_{0} -k a g c d ( a , b ) \frac{a}{gcd(a,b)} ),其中k为任意整数。

第六章 因式分解与算术基本定理

算术基本定理(唯一分解定理)

每个整数n \ge 2可唯一分解成素数成积n= p 1 p_{1} p 2 p_{2} p r p_{r} 。即整数n可以以某种方式分解成素数乘积,且仅有一种这样的分解。

质因数分解

朴素分解

复杂度O( n \sqrt{n} )

map<ll,ll>prime_factor(ll n){
map<ll,ll>res;
for(ll i=2;i*i<=n;i++){
while(n%i==0){
++res[i];
n/=i;
}
}
if(n!=1)res[n]=1;
return res;
}

pollard_rho算法

传说中的随机算法,复杂度O( n 1 / 4 n^{1/4} ),适用于n较大的情况,判断一个大整数是否为素数用Miller_rabin算法,求一个大整数的所有质因数用Pollard_rho算法。
poj1181:求一个整数是否是素数,如果不是,则输出它最小的质因数。

#include<iostream>
#include<cmath>
#include<ctime>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int t;
ll n,prim[1000];
ll ans;
int tot;
ll mul_mod(ll a,ll b,ll c)
{
	ll t=0;
	a%=c;
	b%=c;
	while(b){
		if(b&1){
			t=(t+a)%c;
		}
		a<<=1;
		a%=c;
		b>>=1;
	}	
	return t;
}
//ll pow_mod(ll a,ll b,ll c)
//{
//	ll t=1;
//	while(b){
//		if(b&1) t=(t*a)%c;
//		b>>=1;
//		a=(a*a)%c;
//	}
//	return t;
//}
ll pow_mod(ll x,ll n,ll mod){  
    if (n==1) return x%mod;
    int bit[90],k=0;
    while (n){
        bit[k++]=n&1;
        n>>=1;
    }
    ll ret=1;
    for (k=k-1;k>=0;k--){
        ret=mul_mod(ret,ret,mod);
        if (bit[k]==1) ret=mul_mod(ret,x,mod);
    }
    return ret;
}
bool check(ll a,ll n,ll x,ll t)
{
	ll ret=pow_mod(a,x,n);
	ll tmp=ret;
	for(int i=0;i<t;i++){
		ret=mul_mod(ret,ret,n);
		if(ret==1&&tmp!=1&&tmp!=n-1) return true;
		tmp=ret;
	}
	if(ret!=1) return true;
	return false;
}
bool Miller_rabin(ll n)
{
	ll x=n-1,t=0;
	while((x&1)==0){
		x>>=1;
		t++;
	}
	int ok=1;
	if(t>=1&&(x&1)){
		for(int i=0;i<20;i++){
			ll a=rand()%(n-1)+1;
			if(check(a,n,x,t)){
				ok=1;break;
			}
			ok=0;
		}
	}
	if(!ok||n==2) return false;
	return true;
}
//ll gcd(ll a,ll b)
//{
//	if(b==0) return a;
//	return gcd(b,a%b);
//}
ll gcd(ll a,ll b){
    if (a==0) return 1;
    if (a<0) return gcd(-a,b);
    while (b){
        ll t=a%b; a=b; b=t;
    }
    return a;
}
ll Pollard_rho(ll x,ll c)
{
	ll i=1,k=2;
	ll x0=rand()%x,y=x0;
	while(1){
		i++;
		x0=(mul_mod(x0,x0,x)+c)%x;
		ll d=gcd(y-x0,x);
		if(d>1&&d<x) return d;
		if(y==x0) return x;
		if(i==k){
			y=x0;
			k+=k;
		}
	}
}
void findfac(ll n)
{
	if(!Miller_rabin(n)){
		prim[tot++]=n;
		return;
	}
	ll p=n;
	while(p>=n) p=Pollard_rho(p,rand()%(n-1)+1);
	findfac(p);
	findfac(n/p);
}
int main()
{
	srand(time(NULL));
	scanf("%d",&t);
	while(t--){
		scanf("%lld",&n);
		if(!Miller_rabin(n)){
			printf("Prime\n");
			continue;
		}
		tot=0;
		findfac(n);
		ans=prim[0];
		for(int i=1;i<tot;i++){
			ans=min(ans,prim[i]);
		}
		printf("%lld\n",ans);
	}
}

第七章 同余式

如果m整除a-b,则称a-b模m同余,记为a \equiv b(mod m)
其中m记为同余式的模,具有相同模的同余式,在许多方面表现得像通常的等式,其满足同余式的加减乘法,如 a 1 b 1 a_{1}\equiv b_{1} (mod m)且 a 2 b 2 a_{2}\equiv b_{2} (mod m),
a 1 ± a 2 b 1 ± b 2 a_{1}\pm a_{2}\equiv b_{1}\pm b_{2} (mod m),且 a 1 a 2 b 1 b 2 a_{1}a_{2}\equiv b_{1}b_{2} (mod m)
但是同余式除法仅在除数与模数互素时成立,即ac \equiv bc(mod m)仅在gcd(c,m)=1时成立。

线性同余定理

求解同余式:设a,c与m均是整数,m \ge 1,并设g=gcd(a,m)
\bullet 如果g \nmid c,则同余式ax \equiv c(mod m)无解;
\bullet 如果g|c恰好有g个不同的解,要求这些解则先要求线性方程au+mv=g的一个解(通过扩展欧几里得算法)( u 0 u_{0} v 0 v_{0} ),则 x 0 x_{0} = c u 0 g \frac{cu_{0}}{g} 是该同余式的一个解,
而解的完全集为x \equiv x 0 x_{0} +k \cdot m g \frac{m}{g} (mod m) k=0,1,2,3,…,g-1。

模p多项式根定理

设p为素数,f(x)= a 0 a_{0} xd+ a 1 a_{1} xd-1…= a d a_{d} 是次数为d \ge 1的整系数多项式,则p \nmid a 0 a_{0} ,则同余式f(x)$\equiv$0(mod p)最多又d个模p不同余的解。

第八章 威尔逊定理

当且仅当p为素数时,p|(p-1)!+1。
其逆反命题为当p是合数,则(p-1)! ≢ \not\equiv -1(mod p)。

第九章 同余式、幂与费马小定理

费马小定理

设p为素数,a是任意整数,且a ̸ \equiv\not 0(mod p)(即p \nmid a),则有ap-1 \equiv 1(mod p)

第十章 同余式、幂与欧拉公式

欧拉公式

如果gcd(a,m)=1则
a φ ( m ) a^{\varphi(m)} \equiv 1(mod m),其中 φ ( m ) \varphi(m) 为欧拉函数,意指区间[1,m]之间与m互质的数的个数。

第十一章 乘法逆元

逆元的意义

对于一些题目会要求把结果模一个数,通常是一个较大的质数,对于加减乘法通过同余定理可以直接拆开计算,但对于(a/b)%mod这个式子,是不可以写成 a % m o d b % m o d % m o d \frac{a\%mod}{b\%mod}\%mod 的,但是可以写成(a+b-1) % \% mod,其中b-1表示b的逆元。

费马小定理求逆元

由费马小定理可知,当p为质数,且q \nmid a时有ap-1 \equiv 1(mod p)分解后就可以写成a \cdot ap-2 \equiv 1(mod p),故可知当p为质数,a,p互质时a关于p的逆元就是ap-2,因此通过快速幂求解即可,复杂度O(log(mod))。
费马小定理求逆元模板

ll qkpow(ll a,ll b,ll mod){
ll t=1,tt=a%mod;
while(p){
if(p&1)t=t*tt%mod;
tt=tt*tt%mod;
p>>=1;
}
return t;
}
ll getinv(ll a,ll mod){
return qkpow(a,mod-2,mod);
}

欧拉公式求逆元

与费马小定理同理,当a,p互质,但p不一定为质数时,需要采用欧拉公式使得a-1 a φ ( p ) 1 a^{\varphi(p)-1} ,在模板中将mod-2改为 φ ( m o d ) 1 \varphi(mod)-1 即可,时间复杂度O(log( φ \varphi (mod)))。

扩展欧几里得算法求逆元

当所给的数与模数不互素时,我们可以选择解线性方程的方式来解出一个数对应模数的逆元。
形如a \cdot x \equiv 1(mod p)我们可以转化为ax+py=1,通过扩展欧几里得算法求出x即为对应p时的逆元,复杂度O(log(a))。
扩展欧几里得算法求逆元模板

ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1,y=0;
return a;
}
ll ret==exgcd(b,a%b,y,x);
y-=a/b*x;
return ret;
} 
ll getinv(ll a,ll mod){
ll x,y;
ll d=exgcd(a,mod,x,y);
return d==1?(x%mod+mod)%mod:-1;
}

递推法求逆元

即打表求逆元,令模数为p,原数为i,令t=p/i,k=p%i,则有
t * i+k \equiv 0(mod p)
即-t * i \equiv k(mod p),当i为p的因数时,逆元不存在,不讨论,当i不是p的因数时自然有gcd(ik,p)=1,故可对原式做除法
-t \cdot inv[k] \equiv inv[i](mod p)
将k,t重新代回,则有inv[i]=(p-p/i)*inv[p%i]%p
可在O(n)的时间内推出逆元。
递推法求逆元模板

ll inv[mod+5]
void getinv(ll mod){
inv[1]=1;
for(ll i=2;i<mod;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}

第十二章 欧拉函数与中国剩余定理

欧拉函数

φ(m)为欧拉函数,意指区间[1,m]之间与m互质的数的个数。
φ \varphi 函数公式
{ φ ( p k ) = p k p k 1 p k 1 φ ( m n ) = φ ( m ) φ ( n ) ( g c d ( m , n ) = 1 ) \begin{cases}\varphi(p^{k})=p^{k}-p^{k-1} & p为素数,k\ge 1 \\ \varphi(mn)=\varphi(m)\varphi(n)&(gcd(m,n)=1)\end{cases}

欧拉函数筛

#include<iostream>  
#include<cstdio>  
#define N 40000  
using namespace std;  
int n;  
int phi[N+10],prime[N+10],tot,ans;  
bool mark[N+10];  
void getphi()  
{  
   int i,j;  
   phi[1]=1;  
   for(i=2;i<=N;i++)//相当于分解质因式的逆过程  
   {  
       if(!mark[i])  
           {  
             prime[++tot]=i;//筛素数的时候首先会判断i是否是素数。  
             phi[i]=i-1;//当 i 是素数时 phi[i]=i-1  
             }  
       for(j=1;j<=tot;j++)  
       {  
          if(i*prime[j]>N)  break;  
          mark[i*prime[j]]=1;//确定i*prime[j]不是素数  
          if(i%prime[j]==0)//接着我们会看prime[j]是否是i的约数  
          {  
             phi[i*prime[j]]=phi[i]*prime[j];break;  
          }  
          else  phi[i*prime[j]]=phi[i]*(prime[j]-1);//其实这里prime[j]-1就是phi[prime[j]],利用了欧拉函数的积性  
       }  
   }  
}  
int main()  
{  
    getphi();  
}  


求单个数的欧拉函数值

直接求小于或等于n,且与n互质的个数,时间复杂度O(log(n))。

ll oula(ll n){
ll res=n;
for(ll i=2;i*i<=n;i++)
if(n%i==0){
res=res-res/i;
do
n/=i;
while(n%i==0);
}
if(n>1)res=res-res/n;
return res;
}

中国剩余定理

设m和n是整数,gcd(m,n)=1,b与c为任意整数,则同余数组
x \equiv b(mod m)与x \equiv c(mod n)恰好有一个解0 \leq x < < mn。

中国剩余定理解同余方程组

{ x a 1 ( m o d m 1 ) x a 2 ( m o d m 2 ) . . . x a k ( m o d m k ) \begin{cases}\\x\equiv a_{1}(mod m_{1}) \\x\equiv a_{2}(modm_{2})\\...\\x\equiv a_{k}(mod m_{k})\end{cases}
其中 m 1 m_{1} , m 2 m_{2} m k m_{k} 为两两互质的整数,求x的最小非负整数解。
解:
令M= i = 1 k m i \begin{matrix} \prod_{i=1}^k m_i \end{matrix} ,由于 m i m_{i} 互质,故M为所有 m i m_{i} 的最小公倍数,令 t i t_{i} 为同余方程 M m i \frac{M}{m_{i}} t i t_{i} \equiv 0(mod m i m_{i} )的解,由于 \forall k = ̸ =\not i均有 M m i \frac{M}{m_{i}} t i t_{i} \equiv a i a_{i} (mod m i m_{i} )
故x有解x= i = 1 n a i M m i t i \sum_{i=1}^n a_{i}\frac{M}{m_{i}}t_{i}
故又通解x+i*M(i \in Z)故其最小非负整数解就是(x%M+M)%M
其中 t i t_{i} 即是 M m i \frac{M}{m_{i}} 在模数为 m i m_{i} 时的逆元,故用扩展欧几里得算法算出 t i t_{i} 再做累加即可得出答案。

void exgcd(ll a,ll,b,ll &x,ll &y){
if(b==0){x=1;y=0;return;}
exgcd(b,a%b,x,y);
ll tp=x;
x=y;
y=tp-a/b*y;
}
ll china(){
ll ans=0,lcm=1,x,y;
for(ll i=1;i<=k;i++)lcm*=b[i];
for(ll i=1;i<=k;i++){
ll tp=lcm/b[i];
exgcd(tp,b[i],x,y);
x=(x%b[i]+b[i])%b[i];//x要为最小非负整数解
ans=(ans+tp*x*a[i])%lcm;
}
return (ans+lcm)%lcm;
}

扩展中国剩余定理

前面已经解决了,当 m 1 , m 2 . . . m k m_{1},m_{2}...m_{k} 互素时线性同余方程的解,那么当 m 1 , m 2 . . . m k m_{1},m_{2}...m_{k} 不互素时呢?
如何解余数不互质的线性同余方程组:
首先对起那面两个式子进行处理,我们可将同余式化成 k 1 m 1 + k 2 m 2 = a 2 a 1 k_{1}m_{1}+k_{2}m_{2}=a_{2}-a_{1} ,据前面的扩展欧几里得可知此方程在gcd( m 1 , m 2 ) ( a 2 a 1 ) m_{1},m_{2})|(a_{2}-a_{1}) 时有解,且可以解出 k 1 , k 2 k_{1},k_{2} 同时满足一式和二式,此时可以得到一个同时满足上面两个式子的X= k 1 m 1 + a 1 = k 2 m 2 + a 2 k_{1}m_{1}+a_{1}=k_{2}m_{2}+a_{2} ,我们取X= k 1 m 1 + a 1 k_{1}m_{1}+a_{1} 为代表构造一个数d满足 m 1 d m_{1}|d && m 2 d m_{2}|d ,这样就可以用同余式x \equiv k 1 m 1 + a 1 k_{1}m_{1}+a_{1} (mod d)来代替原来两个式子,由此,令d=lcm( m 1 , m 2 m_{1},m_{2} )即可。
据上,则可以将原式合并成下式
x \equiv k 1 m 1 + a 1 k_{1}m_{1}+a_{1} (mod lcm( m 1 , m 2 m_{1},m_{2} ))。
据此通过迭代即可将所有的等式合并成一个式子得出最终答案。

ll exchina(){
llM=m[1],A=a[i],t,d,x,y;
ll i;
for(i=2;i<=n;i++){
d=exgcd(M,m[i],x,y);
if((a[i]-A)%d)return -1;
x*=(a[i]-A)/d,t=m[i]/d,x=(x%t+t)%t;
A=M*x+A,M=M/d*m[i],A%=M;
}

第十三章 素数

无穷多素数定理

存在无穷多的素数。

模四余三素数定理

存在无数多的模四余三的素数。

算数级数的迪利克雷定理

设a与m为整数,gcd(a,m)=1,则存在无数多的素数模m余a,即存在无数多的素数满足p \equiv a(mod m)。

第十四章 素数的计数

素数定理

当x很大时,小于x的素数个数趋近于 x l n ( x ) \frac{x}{ln(x)} ,即 lim x + {\lim_{x \to +\infty}} π ( x ) x / l n ( x ) \frac{\pi(x)}{x/ln(x)} =1。

哥德巴赫猜想

强哥德巴赫猜想:任一大于2的偶数都可写成两个素数之和。
弱哥德巴赫猜想:任意大于7的奇数都可写成三个素数之和。

孪生素数猜想

存在无数多的素数p使得p+2也是素数,存在无数多的素数p使得p+2为素数或两个素数的乘积。

N^2+1猜想

有无穷多个形如N2+1的素数。

第十五章 梅森素数与完全数

梅森素数

对于整数 a 2 a\ge2 n 2 n\ge2 ,若an-1是素数,则a比等于2,且n必为素数。我们将形如2p-1的素数成为梅森素数。

完全数

因数的和与原本的数相等,则称这个数为完全数。

欧几里得完全数公式

若2p-1为素数,则2p-1(2p-1)为完全数。

σ函数

σ \sigma (x)=n的所有因数之和,则其有公式
{ σ ( p k ) = 1 + p + p 2 + . . . + p k p k 1 σ ( m n ) = σ ( m ) σ ( n ) ( g c d ( m , n ) = 1 ) \begin{cases}\sigma(p^{k})=1+p+p^{2}+...+p^{k} & p为素数,k\ge 1 \\ \sigma(mn)=\sigma(m)\sigma(n)&(gcd(m,n)=1)\end{cases}

欧拉完全数定理

如果n为偶完全数,则n形如2p-1(2p-1),其中2p-1为梅森素数。

第十六章 幂模m与逐次平方法

快速幂

ll pow(ll a,ll n,ll p){
    ll ans=1;
    while(n){
        if(n&1)
            ans=ans*a%p;
        a=a*a%p;
        n>>=1;
    }
    return ans;
}

字符串读取

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll quick_mod(ll a,ll b){
ll ans=1;
while(b){
    if(b&1){
        ans=(ans*a)%mod;
        b--;
    }
    b/=2;
    a=a*a%mod;
}
return ans;
}
ll quickmod(ll a,char *b,ll len){
ll ans=1;
while(len>0){
    if(b[len-1]!='0'){
        ll s=b[len-1]-'0';
        ans=ans*quick_mod(a,s)%mod;
    }
    a=quick_mod(a,10)%mod;
    len--;
}
return ans;
}
int main(){
char s[100050];
ll a;
while(~scanf("%lld",&a)){
    scanf("%s",s);
    ll len=strlen(s);
    printf("%lld\n",quickmod(a,s,len));
}
return 0;
}

欧拉降幂公式

当B \ge ϕ \phi (C)时有:AB % \% C= A B % ϕ ( C ) + ϕ ( C ) A^{B\%\phi (C)+\phi (C)} % \% C

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
char a[1000006];
ll x,z;
ll quickpow(ll x,ll y,ll z){
ll ans=1;
while(y){
    if(y&1){
        ans=(ans*x)%z;
    }
   x=x*x%z;
   y>>=1;
}
return ans;
}
ll phi(ll n){
ll i,res=0;
for(i=2;i*i<=n;i++){
    if(n%i==0){
        res=res-res/i;
        while(n%i==0)
            n/=i;
    }
}
if(n>1)
res=res-res/n;
return res;
}
int main(){
while(~scanf("%lld %s %lld",&x,a,&z)){
    ll len=strlen(a);
    ll p=phi(z);
    ll ans=0;
    for(ll i=0;i<len;i++)
        ans=(ans*10+a[i]-'0')%p;
    ans+=p;
    printf("%lld\n",quickpow(x,ans,z));
}
return 0;
}

第十七章 快速乘

快速计算a*b%mod的结果,对于大数直接乘可能会爆long long,用快速乘每一步都取余不会爆掉。

ll q_mul(ll a,ll b,ll mod){
ll ans=0;
while(b){
if(b&1)ans=(ans+a)%mod;
a=a(a<<1)%mod;
b>>=1;
}
return ans;
}

第十八章 计算模m的k次根

设b,k,m为已知整数,满足gcd(b,m)=1且gcd(k, ϕ \phi (m))=1,则可以通过以下步骤得出同余式
x k b x^{k}\equiv b (mod m)
1.计算 ϕ \phi (m);
2.求满足ku- ϕ \phi (m)v=1的正整数u与v;
3.x=bu(mod m)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
char a[1000006];
ll x,z;
ll quickpow(ll x,ll y,ll z){
ll ans=1;
while(y){
    if(y&1){
        ans=(ans*x)%z;
    }
   x=x*x%z;
   y>>=1;
}
return ans;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0)x=1,y=0;
else{
    exgcd(b,a%b,y,x);
    y-=a/b*x;
}
}
ll phi(ll n){
ll i,res=0;
for(i=2;i*i<=n;i++){
    if(n%i==0){
        res=res-res/i;
        while(n%i==0)
            n/=i;
    }
}
if(n>1)
res=res-res/n;
return res;
}
int main(){
    ll x,k,b,m;
    ll xx,yy;
while(cin>>k>>b>>m){
    ll phim=phi(m);
    exgcd(k,phim,xx,yy);
    xx=(xx%phim+phim)%phim;
    cout<<xx<<endl;
    x=quickpow(b,xx,m);
    cout<<x<<endl;
    cout<<quickpow(x,k,m)<<endl;
}
return 0;
}

第十九章 素数测试与卡米歇尔数

根据费马小定理,假如p为素数,那么对所有的整数a都有ap \equiv a(mod p),则可以通过不断选取a,若出现不满足这个条件,则这个数就是合数,但是存在一种卡米歇尔数,其虽然为合数,但是任意的a都满足上述同余式。

卡米歇尔数

卡米歇尔数是可以冒充素数的一种合数,其没有任何可以用费马测试法判断出为合数的证据。
素数的一个性质
设p为一个奇素数,记p-1=2kq,q为奇数,
设a为不被p整除的整数,则下述的两个条件之一成立:
1.aq%p=1;
2.数aq,a2q, a 2 2 q a^{2^{2}q} ,…, a 2 k 1 q a^{2^{k-1}q} 之一模p余-1。

合数的拉宾-米勒测试

素数的一个性质
设p为一个奇素数,记p-1=2kq,q为奇数,
设a为不被p整除的整数,则下述的两个条件之一成立:
1.aq%p=1;
2.数aq,a2q, a 2 2 q a^{2^{2}q} ,…, a 2 k 1 q a^{2^{k-1}q} 之一模p余-1。
设p为奇素数,记p-1=2kq,q为奇数,对不被p整除的某个a,若上述两个条件都不满足,则p为合数。

ll prime[6]={2,3,5,233,331};
ll qmul(ll x,ll y,ll mod){
ll res=1;
while(n){
if(n&1)res=qmul(res,a,mod);
a=qmul(a,a,mod);
a>>=1;
}
return res;
}
bool miller_rabin(ll p){
if(p<2)return 0;
if(p!=2&&p%2==0)return 0;
ll s=p-1;
while(!(s&1))s>>=1;
for(ll i=0;i<5;i++){
if(p==prime[i])return 1;
ll t=s,m=qpow(prime[i],s,p);
while(t!=p-1&&m!=1&&m!=p-1){
m=qmul(m,m,p);
t<<=1;
}
if(m!=p-1&&!(t&1))return 0;
}
return 1;
}

素数筛法

埃氏筛法

bool prime[N];
void init(){
for(ll i=2;i<N;i++)prime[i]=true;
for(ll i=2;i<N;i++){
if(prime[i]){
for(ll j=i*2;j<N;j+=i){
prime[j]=false;
}
}
}
}

线性筛法

bool prime[N];
ll p[N],tot;
void init(){
for(ll i=2;i<N;i++)prime[i]=true;
for(ll i=2;i<N;i++){
if(prime[i])p[tot++]=i;
for(ll j=0;j<tot&&i*p[j]<N;j++){
prime[i*p[j]]=false;
if(i%p[j]==0)break;
}
}
}

第二十章 模p平方剩余

二次剩余与二次非剩余

与一个平方数模p同余的,不是p的倍数的数称为模p的二次剩余,不与任何一个平方数模p同余的数称为模p的二次非剩余,我们将二次剩余记作QR,二次非剩余记作NR。
定理:设p为一奇素数,则恰有 p 1 2 \frac{p-1}{2} 个模p的二次剩余,且恰有 p 1 2 \frac{p-1}{2} 个模p的二次非剩余。

二次剩余乘法法则

勒让德符号:勒让德发现QR和NR的乘法性质同+1和-1很像,因此他引出了如下符号:
a p \frac{a}{p} )= { 1 a p 1 a p \begin{cases}1&若a是模p的二次剩余 \\ -1&若a是模p的二次非剩余\end{cases}
设p为奇素数,则
( a p \frac{a}{p} )*( b p \frac{b}{p} )=( a b p \frac{ab}{p} )。

欧拉准则

设p为奇素数,则
a p 1 2 ( a p ) a^{\frac{p-1}{2}}\equiv(\frac{a}{p}) (mod p),其中 a p \frac{a}{p} 为勒让德符号。

二次剩余Cipolla算法

在介绍CIpolla算法之前我们需要先引入一些定理或引理,算法的用处是解x2 \equiv n(mod p)因此下面所称的方程均为左式,下面的引理或定理中,所有p均为奇素数。
引理一:n(p-1)/2 \equiv ± \pm 1(mod p)。
证明:即欧拉准则,一个数或为二次剩余数,或为二次非剩余数。
引理二:方程x2 \equiv n(mod p)有解当且仅当n(p-1)/2 \equiv 1(mod p)
证明:方程有解则n为二次剩余。
引理三:设a满足 ω \omega =a2-n不是模p的二次剩余,即x2 \equiv ω \omega (mod p)无解,则x \equiv ( a + ω ) p 1 2 (a+\sqrt{\omega})^{\frac{p-1}{2}} 是方程的解。

#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long LL;
struct pii{
	int x,y;
	pii(int x=0,int y=0):x(x),y(y){}
};
LL w;
pii multi(pii a,pii b,LL p)
{
	return pii((a.x*b.x%p+a.y*b.y%p*w%p)%p,(a.x*b.y%p+a.y*b.x%p)%p);
}
LL quick_pow_D(pii a,LL b,LL p)
{
	pii ans(1,0);
	while(b)
	{
		if(b&1) ans=multi(ans,a,p),b--;
		a=multi(a,a,p);
		b>>=1;
	}
	return ans.x;
}
LL quick_pow(LL a,LL b,LL p)
{
	LL ans=1;a%=p;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
int check(LL n,LL p)
{
	return quick_pow(n,(p-1)/2,p);
}
LL mod(LL a,LL n)
{
	a%=n;
	if(a<0) return a+n;
	return a;
}
void solve(int b,int p)
{
	LL a,t;
	while(1)
	{
		a=rand()%p;
		t=a*a-b;
		w=mod(t,p);
		if(check(w,p)==p-1) break;
	}
	pii temp(a,1);
	int ans=quick_pow_D(temp,(p+1)/2,p);
	int ans2=p-ans;
	if(ans>ans2) swap(ans,ans2);
	printf("%d %d\n",ans,ans2);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int a,n;
		scanf("%d%d",&a,&n);a%=n;
		if(n!=2&&check(a,n)==n-1) printf("No root\n");
		else if(n==2)
		{
			if(a==1) printf("1\n");
			else printf("No root\n");
		}
		else
		{
			solve(a,n);	
		}
	}
}

第二十一章 离散对数

BSGS算法

BSGS算法全称Baby-Step-Giant-Step,即大小步算法,可以快速求解离散对数问题,形如
Ax \equiv B(mod C),其中C为素数,以下所有讨论均基于gcd(a,c)=1的前提(C为素数,故也即A为C的倍数,可以较容易地特判掉)。
首先引入一个引理 A x m o d    ϕ ( C ) A x ( m o d    C ) A^{x\mod\phi (C)}\equiv A^{x}(\mod C) 这由欧拉公式可以很容易得出,由此若 0 x ϕ ( c ) 0\le x\le \phi (c) 无解那么更大的区域也一定无解了(当然 ϕ \phi ( C)也不是Ax的最小循环节,这一点结合原根的一些性质是很容易发现的),接下来我们需要一些分块的思想,我们首先需要设m=[ ϕ ( C ) \sqrt{\phi (C)} ],我们假设x=i*m-j( 0 i < m , 0 j < m , i , j N 0\le i< m,0\le j< m,i,j\in N ),也即Ax=Ai+m/Aj则原式可以转化为Aj*B \equiv Ai+m(mod C),再打表枚举j为不同值时Aj*B的值,存入hash表中,再枚举i第一个出现在hash表中的结果即为解。

#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
    long long k,m;
    map<long long,long long>mp;
inline long long multi(long long x,long long y,long long mod) //快速乘
{
    long long tmp=(x*y-(long long)(((long double)x*y+0.5)/mod)*mod);
    if (tmp<0) return tmp+mod; else return tmp;
}
long long quickpower(long long a,int b)
{
    long long t=1;
    while (b>0)
    {
        if ((b&1)==1) t=multi(t,a,m);
        if (b>1) a=multi(a,a,m);
        b=b>>1;
    }
    return t;
}
int main()
{
    mp.clear();
    scanf("%lld%lld",&k,&m);
    long long now=(9*k+1)%m;
    mp[now]=0;
    int mm=ceil(sqrt(m));
    for (int i=1;i<=mm;i++) //预处理哈希表
    {
        now=multi(now,10,m);
        mp[now]=i;
    }
    now=1;
    long long q=quickpower(10,mm);
    for (int i=1;i<=mm;i++)
    {
        now=multi(now,q,m);
        if (mp[now])
        {
            printf("%lld",(((long long)(i)*(long long)(mm)-mp[now])%m+m)%m);
            return 0;
        }
    }
    return 0;
}
 

扩展BSGS算法

BSGS只能解决C为素数的情况,为此引入扩展BSGS
令d=gcd(A,C),则A=ad,B=bd,C=cd若B不是d的倍数且非1则无解,
由于A
x \equiv Bx(mod Cx) \to A \equiv B(mod C),故可以不断提取Ax与C的公因式,递归处理至Ax与C互质,将原方程处理至DAz \equiv B(mod C)这时再用普通的BSGS算法求解即可记cnt=x-z,最后得出的答案再将cnt加回即可,需要注意的是,这样得出的解仅有仅有大于cnt的解,因此为了不漏解还需要枚举1-cnt。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
map<int, int> h;
int gcd(int a, int b) {return b ? gcd(b, a % b) : a;}
inline ll ksm(ll a, ll b, ll mod)
{
    ll ret = 1, h = a;
    while(b)
    {
        if(b & 1)
            ret *= h, ret %= mod;
        h *= h, h %= mod, b >>= 1;
    }
    return ret;
}
inline int exbsgs(int a, int b, int p)
{
    a %= p, b %= p;
    if(b == 1) return 0; 										//特判2
    int g, d = 0;
    ll c = 1;
    while((g = gcd(a, p)) > 1)
    {
        if(b % g) return -1;
        b /= g, p /= g, c = c * (a / g) % p, ++d;
        if(c == b) return d; 						//特判3(其实等价于特判2)
    }
    h.clear();
    int t = int(sqrt(p * 1.0) + 1);
    ll base = b;									//计算右半部分的值
    for(int i = 0; i < t; i++)
        h[base] = i, base = base * a % p;
    base = ksm(a, t, p); 							//用左半边的值验证
    ll now = c;
    for(int i = 1; i <= t + 1; i++)
    {
        now = now * base % p;
        if(h.count(now))
            return i * t - h[now] + d;				//返回解
    }
    return -1;										//无解
}
int main()
{
    int a, b, p;
    while(~scanf("%d%d%d", &a, &p, &b) && a && b && p)
    {
        if(p == 1) {puts("0"); continue;} 			//特判1
        int ans = exbsgs(a, b, p);
        if(ans == -1) puts("No Solution");
        else printf("%d\n", ans);
    }
}

第二十二章 佩尔方程

形如x2-D \cdot y2=1(D是一个固定的正整数且D不是完全平方数的方程称为佩尔方程。

佩尔方程定理

设D是一个整数,且不是完全平方数,则佩尔方程x2-D \cdot y2=1总有正整数解,如果( x i x_{i} , y i y_{i} )是使得 x i x_{i} 最小的解,则每个解可以通过去幂得到:
x k + y k D = ( x 1 + y 1 D ) k x_{k}+y_{k}\sqrt{D}=(x_{1}+y_{1}\sqrt{D})^{k} ,(k=1,2,3…)
若D是完全平方数,则佩尔方程仅能得出一组通解:x=1,y=0。

连分数与佩尔方程

连分数

一个简单的实数可以表示为
x = a 0 + 1 a 1 + 1 a 2 + 1 a 3 + . . . x=a_{0}+\frac{1}{a_{1}+\frac{1}{a_{2}+\frac{1}{a_{3}+...}}}
为了方便,我们可以将它简记为X=[ a 0 ; a 1 , a 2 , . . . a_{0};a_{1},a_{2},... ]
连分数有性质:所有的无限连分数均是无理数,所有的无理数都可以一种精确的方式表示成无限连分数,可以用这种方法逼近一个无理数的值。

非完全平方数的平方根的连分数表示

一个非完全平方数的平方根的连分数都是以周期呈现的,如:
22 \sqrt{22} =[4;1,2,4,2,1,8,…],在之后就会循环出现1,2,4,2,1,8,我们不妨将这种形式记为 22 \sqrt{22} =[4;<1,2,4,2,1,8>],循环节长度为6,且为纯循环,循环节的最后一个数必然是 a 0 a_{0} 的两倍。

求佩尔方程的最小特解

D = [ a 0 ; a 1 , a 2 , . . . , a n 1 , 2 a 0 ] \sqrt{D}=[a_{0};a_{1},a_{2},...,a_{n-1},2a_{0}]
我们记一有理数 p q = [ a 0 ; a 1 , a 2 , . . . , a n 1 ] \frac{p}{q}=[a_{0};a_{1},a_{2},...,a_{n-1}]
设其循环节长度为n,则其解就有
{ x 0 = p , y 0 = q n % 2 = 0 x 0 = 2 p 2 + 1 , y 0 = 2 p q n % 2 = 1 \begin{cases}x_{0}=p,y_{0}=q&n\%2=0 \\ x_{0}=2p^{2}+1,y_{0}=2pq&n\%2=1\end{cases}
由此得出佩尔方程的解。

int a[20000];
bool pell(int &x,int &y,int d){
    int m=(int)sqrt((double)d);
    if(m*m==d)//d不能为完全平方数
        return false;
 
    //将d以连分数形式存储
    int num=0;//连分数数位
    double sq=sqrt(d);//d的高精度根,相当于r0
    a[num++]=m;//存储整数部分
    int b=m;//当前整数部分
    int c=1;//连分数最终展开时的分母
    double temp;//连分数展开时的每一项
    do{
        c=(d-b*b)/c;
        temp=(sq+b)/c;
        a[num++]=(int)(floor(temp));
        b=a[num-1]*c-b;
    }while(a[num-1]!=2*a[0]);//当有一位等于整数两倍时结束
 
    //将连分数形式化为分子分母形式,即求p、q两个值
    int p=1,q=0;
    for(int i=num-2;i>=0;i--){
        int temp=p;
        p=q+p*a[i];
        q=temp;
    }
 
    if((num-1)%2){//连分数长度为奇数时
        x=2*p*p+1;
        y=2*p*q;
    }
    else{//连分数长度为偶数时
        x=p;
        y=q;
    }
    return true;
}
int main(){
    int d;
    while(scanf("%d",&d)!=EOF){
        int x,y;
        if(pell(x,y,d))
            cout<<x<<" "<<y<<endl;
        else
            cout<<"No Solution"<<endl;
    }
    return 0;
}

第二十三章 阶与原根

定义:若a与m是正整数,gcd(a,m)=1,使得ax \equiv 1(mod m)成立的最小的x,称为a对模m的阶,记 δ m ( a ) \delta_{m}(a)
定理:
1.若m>0,gcd(a,m)=1,正整数x是同余式ax \equiv 1(mod m)的一个解当且仅当 δ m ( a ) n \delta_{m}(a)|n
2.如果a与m是互质的整数且m>0,那么正整数ai \equiv aj( m o d    m \mod m ),当且仅当i \equiv j(mod δ m ( a ) \delta_{m}(a) )

原根

定义:设m是正整数,a是整数,若a模m的阶等于 φ \varphi (m),则称a为模m的一个原根。
定理:
1.若模m有原根,那么它一共有 φ ( φ ( m ) ) \varphi(\varphi(m)) 个原根;
2.如果p为素数,那么p一定存在原根,并且模p的原根个数为 φ \varphi (p-1);
3.m有原根的充要条件:m=2,4,pa,2pa,其中p是奇素数;
4.如果 a 与 m是互质的整数且n>0,则如果a是模m的一个原根,那么整数 a 1 a_{1} , a 2 a_{2} , … , a φ ( m ) a_{φ(m)} 构成模m的既约剩余系。
既约剩余类,即简化剩余类,是指在每个模m的值与m互质的剩余类中,各取一数组成的集合。
这个定理说明了关于原根的一个基本性质,即 a i a_{i} 两两互不相同。
5.假设一个数g对于模p来说是原根,那么gi mod p的结果两两不同,且有 1 < g < p 1<g<p , 0 i < p 0\le i<p ,那么g可以称为模的一个原根,归根到底就是gp-1 \equiv 1(mod p)当且仅当指数为p-1的时候成立,这里的p是素数。

求模素数p的原根

对p-1进行因子分解,
即p-1= p 1 a 1 p 2 a 2 . . . p k a k p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{k}^{a_{k}} 是p-1的标准分解式,若恒有 g p 1 p i = ( m o d    p ) g^{\frac{p-1}{p_{i}}}=\not 1(\mod p) 成立,则g就是p的原根。(对于合数求原根,只需要把p-1换成 φ \varphi §即可)。

#include <iostream>  
#include <string.h>  
#include <algorithm>  
#include <stdio.h>  
#include <math.h>  
#include <bitset>  
  
using namespace std;  
typedef long long LL;  
  
const int N = 1000010;  
  
bitset<N> prime;  
int p[N],pri[N];  
int k,cnt;  
  
void isprime()  
{  
    prime.set();  
    for(int i=2; i<N; i++)  
    {  
        if(prime[i])  
        {  
            p[k++] = i;  
            for(int j=i+i; j<N; j+=i)  
                prime[j] = false;  
        }  
    }  
}  
  
void Divide(int n)  
{  
    cnt = 0;  
    int t = (int)sqrt(1.0*n);  
    for(int i=0; p[i]<=t; i++)  
    {  
        if(n%p[i]==0)  
        {  
            pri[cnt++] = p[i];  
            while(n%p[i]==0) n /= p[i];  
        }  
    }  
    if(n > 1)  
        pri[cnt++] = n;  
}  
  
LL quick_mod(LL a,LL b,LL m)  
{  
    LL ans = 1;  
    a %= m;  
    while(b)  
    {  
        if(b&1)  
        {  
            ans = ans * a % m;  
            b--;  
        }  
        b >>= 1;  
        a = a * a % m;  
    }  
    return ans;  
}  
  
int main()  
{  
    int P;  
    isprime();  
    while(cin>>P)  
    {  
        Divide(P-1);  
        for(int g=2; g<P; g++)  
        {  
            bool flag = true;  
            for(int i=0; i<cnt; i++)  
            {  
                int t = (P - 1) / pri[i];  
                if(quick_mod(g,t,P) == 1)  
                {  
                    flag = false;  
                    break;  
                }  
            }  
            if(flag)  
            {  
                int root = g;  
                cout<<root<<endl;  
            }  
        }  
    }  
    return 0;  
}  

第二十四章 整除性质

\bullet n i \left \lfloor \frac{n}{i} \right \rfloor 只有 n \sqrt{n} 种取值, 1 i n 1\leq i\leq n
\bullet n n i \left \lfloor \frac{n}{\left \lfloor \frac{n}{i} \right \rfloor} \right \rfloor n i \left \lfloor \frac{n}{i} \right \rfloor 取值相同的一段区间的右端点。此性质常用于数论分块。
\bullet n a b \left \lfloor \frac{n}{ab} \right \rfloor = n a b \left \lfloor \frac{\left \lfloor \frac{n}{a} \right \rfloor}{b} \right \rfloor = n b a \left \lfloor \frac{\left \lfloor \frac{n}{b} \right \rfloor}{a} \right \rfloor
\bullet n a b \left \lceil \frac{n}{ab} \right \rceil = n a b \left \lceil \frac{\left \lceil \frac{n}{a} \right \rceil}{b} \right \rceil = n b a \left \lceil \frac{\left \lceil \frac{n}{b} \right \rceil}{a} \right \rceil

第二十五章 数论函数之加性函数

数论函数定义

定义域为整数,陪域为复数的函数,其中非常重要的一部分就是积性函数。

加性函数

对于一个函数f,如果对于gcd(a,b)=1满足f(ab)=f(a)+f(b),就称其为加性函数。
常见加性函数
\bullet 质因子数
ω ( n ) = p   n 1 \omega(n)=\sum_{p\ | n}1
p为质数,gcd(a,b)=1说明a,b没有相同的质因子,所以这是加性函数。
\bullet 质因子之和
a 1 ( n ) = p   n p a_1(n)=\sum_{p\ |n} p
p为质数

完全加性函数

对于一个函数f,如果对于任意a,b满足f(ab)=f(a)+f(b),就称其为完全加性函数。
常见完全加性函数:
\bullet 可相同质因子数
Ω ( n ) = p   n t \Omega(n)=\sum_{p\ |n}t
p为质素,t为最大的整数满足pt|n
两数相乘,质因子数量可以直接合并。
\bullet 可相同质因子之和
a 0 ( n ) = p   n p   t a_0(n)=\sum_{p\ |n}p\ *t
p为质素,t为最大的整数满足pt|n

第二十六章 数论函数之积性函数

积性函数

现在已知一个函数为数论函数,且有f(1)=1,则当其满足对于两个互质的数p和q都满足f(p \cdot q)=f ( p) \cdot f(q),称之为积性函数。
无需p,q互质也满足f(p \cdot q)=f ( p) \cdot f(q),则称其为完全积性函数。

常见积性函数

μ(n)——莫比乌斯函数

μ ( n ) \mu(n) = { 1 n = 1 ( 1 ) k n = p 1 p 2 . . . p k 0 \begin{cases}1&n=1 \\ (-1)^{k}&n=p_{1}p_{2}...p_{k} \\ 0& 其余情况 \end{cases}
其可以用于一些有关素数的计数问题的容斥,以及莫比乌斯反演。

φ(n)——欧拉函数

小于n且与n互质的数的数量。

d(n)——约数函数

n的约数的个数。

σ(n)——约数和函数

n的约数的和。

常见完全积性函数

e(n)——元函数

  e ( n ) = [ n = = 1 ] \ e(n)=[n==1]

I(n)——恒等函数

  I ( n ) = 1 \ I(n)=1

id(n)——单位函数

  i d ( n ) = n \ id(n)=n

第二十七章 迪利克雷卷积

在算数函数集上,可以定义一种二元运算,使得这种运算为乘法,取普通函数加法为加法,使得算术函数集为一个交换环,其中一种这样的运算就是迪利克雷卷积。
( f g ) ( n ) = d n f ( d ) g ( n d ) (f*g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})

运算

迪利克雷卷积满足
\bullet 交换律:f   \ * g=g   \ * f
\bullet 结合率:(f   \ * g)   \ * h=f   \ * (g   \ * h)
\bullet 分配律:f   \ * (g+h)=f   \ * g+f   \ * h=(g+h)   \ * f
\bullet 元函数与任意函数的卷积为函数本身f=f   \ * e
\bullet 对于任意数论函数如有 f ( 1 ) = f(1)=\not0 都有唯一的逆函数f-1,使得f   \ * f-1=e,f-1定义为
f-1(n)= { 1 f ( n ) n = 1 1 f ( 1 ) d n , n = f ( n d ) f 1 ( d ) n > 1 \begin{cases}\frac{1}{f(n)}&n=1 \\ \frac{-1}{f(1)}\sum_{d|n,n=\not d}f(\frac{n}{d})f^{-1}(d)&n>1 \\ \end{cases}

常用卷积举例

1.莫比乌斯函数卷积恒等函数为元函数
μ     I =   e \mu\ *\ I=\ e
也就是说   I μ \ I和\mu 互为逆元。
2.欧拉函数卷积恒等函数为单位函数
φ     I =   i d \varphi\ *\ I=\ id
同理也有 φ =   i d   μ \varphi=\ id\ *\mu
根据这个式子可以推出另一个非常巧妙的公式
φ ( n ) n = d   n μ ( d ) d \frac{\varphi(n)}{n}=\sum_{d\ | n}\frac{\mu(d)}{d}
3.恒等函数卷积恒等函数为约数个数函数
  I     I =   d \ I\ *\ I=\ d
同理也有
  I =   d   μ \ I=\ d\ *\mu
4.恒等函数卷积单位函数为约数和函数
  I     i d = σ \ I\ *\ id=\sigma
同理也就有
  i d = σ   μ \ id=\sigma\ *\mu
5. (   i d φ )     i d =   i d 2 (\ id\cdot\varphi)\ *\ id=\ id^{2}
同理 (   i d 2 φ )     i d 2 =   i d 3 (\ id^{2}\cdot\varphi)\ *\ id^{2}=\ id^{3}
以此类推。

第二十八章 莫比乌斯反演

对于数论函数f(n)和F(n)满足
F(n)= d   n f ( d ) \sum_{d\ |n}f(d)
则定义其莫比乌斯反演为
f(n)= d   n μ ( d ) F ( n d ) \sum_{d\ |n}\mu(d)F(\frac{n}{d})
还有一种更常见的反演形式为
f(n)= n   d μ ( d n ) F ( d ) \sum_{n\ |d}\mu(\frac{d}{n})F(d)

第二十九章 杜教筛

有时我们需要在低于线性的复杂度下解决一些积性函数求和的问题,这时候就需要用到杜教筛。
求:
i = 1 n f ( i ) ( n 1 0 9 ) \sum_{i=1}^nf(i)(n\le 10^{9}) (f为积性函数)
分析:
如果可以使用线性复杂度,则这个问题将会非常容易解决,但是109的数据范围显然不允许我们使用线性的复杂度。为了解决这个问题我们需要先构造另外两个函数h,g使其有h=f   \ * g,其中h,g函数都需要容易求前缀和。
设S(n)= i = 1 n f ( i ) \sum_{i=1}^nf(i) ,我们对h函数求前缀和
i = 1 n h ( i ) = i = 1 n d   i g ( d ) f ( i d ) \sum_{i=1}^n h(i)=\sum_{i=1}^n \sum_{d\ |i}g(d)f(\frac{i}{d})
= d = 1 n g ( d ) i = 1 n d f ( i ) =\sum_{d=1}^n g(d) \sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}f(i)
= d = 1 n g ( d ) S ( n d ) =\sum_{d=1}^n g(d)S(\left \lfloor \frac{n}{d} \right \rfloor)
= g ( 1 ) S ( n ) + d = 2 n g ( d ) S ( n d ) =g(1)S(n)+\sum_{d=2}^n g(d)S(\left \lfloor \frac{n}{d} \right \rfloor)
通过移项可得
g(1)S(n)= i = 1 n g ( d ) S ( n d ) \sum_{i=1}^n g(d)S(\left \lfloor \frac{n}{d} \right \rfloor)
已知g和h的前缀和之后就可以通过整数分块递归地求解这个问题,这个算法的复杂度大约是O( n 2 3 n^{\frac{2}{3}} )

第三十章 min_25筛

虽然min_25筛乃至杜教筛名字上都有一个筛字,但是实际上并不能称作一种筛,准确的说,其应该是一种可以亚线性地复杂度下求出积性函数前缀和的算法。其中min25求解积性函数前缀和的而算法。其中min_25筛求解积性函数前缀和时需要满足条件。
\bullet f(x)当x为质数时有一个多项式表示
\bullet f(xc)在x为质数时可以快速计算。

对于答案,先考虑素数贡献,我们定义sp(n)表示 i = 1 n f ( p i ) \sum_{i=1}^nf(p_i) 即前n个素数的积性函数和

这里我们先假设f对于质数的计算是完全积性函数

Pi表示线筛求出的第i小的质数

令g(n,i)表示
j = 2 n \sum_{j=2}^n [j的最小质因数> p i p_i 或j是质数]f(j)
在这里f(j)表示假设j是质数,以质数方式带入函数计算的结果

由于合数会被筛掉因而不会影响答案

考虑怎么计算g(n,i)
类似线性筛的方式每次筛掉一批合数

如果 p i 2 p_i^2 >n则有g(n,i)=g(n,i−1)
因为第i个质数能筛掉的最小合数是 p i 2 p_i^2
因此筛质数只需要筛到 n \sqrt{n} 即可

如果 p i 2 p_i^2\leq n有
g(n,i)=g(n,i−1)−f( p i p_i )∗( g( n p i \frac{n}{p_i} ,i−1)− s p i 1 sp_{i−1} )
原理是假设 p i p_i 是一个质因数,它能产生的合数贡献是f( p i p_i )∗g( n p i \frac{n}{p_i} ,i−1)
但是由于 p i p_i 不一定是最小质因数,还要加回多减的小质数即 s p i 1 sp_{i-1}
由于满足f是完全积性函数,上面部分还算挺清真的

我们需要求的只是g(x,INF)
注意我们发现我们需要求的g(x,INF)只需要满足存在d使得x= n d \frac{n}{d} 即可

可以提前整数分块这样只需要计算 n \sqrt{n} 数量级的g(x,INF)即可

可以通过滚动数组递推的方式完成这一部分
我们令S(n,m)表示 i = 2 n \sum_{i=2}^n [i的最小质因数≥ p m p_m ]f(i)
显然我们要求的是S(n,1)
递归求解

贡献分两步统计:

质数贡献:
g(n,INF)−sp(m−1)
即去掉较小的质数以外其他质数都会被计算到

合数贡献:
k = m p k 2 n e = 1 p k e + 1 n f ( p k e ) S ( n p k e , k + 1 ) + f ( p k e + 1 ) \sum_{k=m}^{p_k^2 \leq n}\sum_{e=1}^{p_k^{e+1}\leq n} f(p_k^e)S(\frac{n}{p_k^e},k+1)+f(p_k^{e+1})
即枚举当前选择的最小质因数以及数量转移,同时计算只选择多于两个当前因数即不往后转移的合数情况
这样直接转移就好了
例子:筛 i = 1 n φ ( i ) \sum_{i=1}^n \varphi (i)
发现phi( p i p_i )= p i p_i -1即对于指数的计算不是一个完全积性函数,这时候需要拆开计算,分别筛质数求值然后相减即可
推S(n,m)的时候也不会有影响,筛 i = 1 n μ ( i ) \sum_{i=1}^n\mu(i) 等其余函数同理。

END
/*
撒花,终于更完了,恭喜自己数论的学习入土成功,也恭喜自己完成了第一篇完整的博客。
后续可能会有少许的修改和补充,应该不会大改了。
文章是根据本人的学习路线写(拼)的,基本是学一点,记一点,所以会比较乱,知识点的分类和顺序处理的不好,抱歉。
主要参考b站上的CSU-ICPC集训课程,IOI国家队论文,csdn和博客园的大佬们的博客,以及本(弱)校学长扔给我们的ppt。
板子一部分是网上抄来的,很多没用过,还有一部分手打,正确性不敢保证QAQ。
还有很多坑没有填,还有很多知识没有学,任重而道远,希望可以在这个过程中收获进步。
虽然应该不会有人看我写的东西,但还是希望自己可以坚持写下去,记录自己的学习历程,嘤嘤嘤。
*/

发布了29 篇原创文章 · 获赞 4 · 访问量 707

猜你喜欢

转载自blog.csdn.net/qq_44290978/article/details/99176397