數論筆記
定義
一些規定
1.如無特殊標記,\(f_k(n)=f^k(n)\)
2.如無特殊說明,\(num(n,p)=max(k \in {p^k|n})\)
3.\([x]\)按照語境通常是向下取整的含義
4.\(\,p\,\)表示容易質數,\(\,P\,\)表示質數集,如無特殊說明,\(\,p_i\,\)表示第\(\,i\,\)大的質數
許多數論函數
歐拉函數\(\varphi(x)\):[1,n]中與n互質的數的個數
劉維爾函數
莫比烏斯函數
約數個數函數
元函數
恆等函數
單位函數
約數和函數
積性函數:
完全積性函數:
狄利克雷卷積
定義
設\(f,g\)為積性函數
則\(f\,\ast\,g=\sum_{d|n}f(d)g(\frac{n}{d})\)
狄利克雷卷積的一些性質
\(f\,\ast\,\epsilon\,=\,f\)
\(f\,\ast\,g=g\,\ast\,f\)
\(f\,\ast\,(g\,\ast\,h)=(f\,\ast\,g)\ast\,h\)
\((f+g)\,\ast\,h=\,f\,\ast\,h\,+\,g\,\ast\,h\)
定理
小知識
常見結論
神秘小知識
(1)
證明如下:
構造一個二元數列\(\,(h,k)\,\)使得\(\,(h,k)=1\,\and\,k\mid n\,\and\,1\leq h<k\),特殊地,\((1,1)\)也被認為是合法的
構造另一個二元數列\(\,(i,n)\,\)其中\(\,1\leq i\leq n\),
對於\(\,(h,k)\,\)考慮其到\(\,(i,n)\,\)的映射,
不難發現\(\,(h,k)\rightarrow (\frac{hn}{k},n)\)
可以得到\(\,|(h,k)|\leq|(i,n)|\,\)
再考慮\(\,(i,n)\,\)到\(\,(h,k)\,\)的映射
可以發現\(\,|(i,n)|\leq|(h,k)|\,\)
放縮得\(\,|(i,n)|=|(h,k)|\,\)
顯然有\(\,|(i,n)|=n\,\),所以\(\,|(h,k)|=n\,\)
結合定義知\(k=d\Rightarrow (h,k)=\varphi(d)\),即\(|(h,k)|=\sum_{d\mid n}\varphi(d)\)
得證
(2)
證明如下
若\(n\neq1\rightarrow n=\prod p_i^{a_i}\)
由\(\mu\,\)的定義知,若\(\,\,\exists\,num(m,p)>1\rightarrow \mu(m)=0\)
故而我們只考慮\(\,\,\prod\limits_{i=1}^{\lambda(n)}[num(m,p_i)\leq 1]=1\,\)的\(\,m\,\),下面的\(\,m\,\)都滿足這一性質
設
就是說\(\,S_i\,\)考慮有\(\,i\,\)個質因子的\(\,m\,\)的總貢獻,\(\,L_i\,\)表示這樣的\(\,m\,\)的個數,特殊地,我們有\(L_0=S_0=1\),也就是\(\,m=1\,\)的貢獻
由組合數的知識容易得到
考慮用\(\,L_i\,\)來表達\(\,I\,\ast\,\mu\,\),簡單帶入可得
考慮構造函數
所以有
綜上,得證
(3)
\(gcd(n,m)=1\rightarrow\varphi(nm)=\varphi(n)\varphi(m)\quad\)也就是說歐拉函數是積性函數,但不是完全積性函數
(4)
杜教篩的構造
要求\(\,f\,\)的前綴和,構造一個很好求前綴和的函數\(\,h\,\)和一個很好求前綴和的輔助函數\(\,g\,\),使得
按狄利克雷卷積的定義展開
考慮用\(\,S_h\,\)表示\(\,S\),於是考慮交換求和符號,使得\(\,g\,\)和\(\,f\,\)分離:
發現\(\,\frac{n}{1}=n\,\),所以我們把右邊第一項提出來:
整理得
由於構造的\(S_h(n)\)是容易求得的,即可以在低於線性的時間內求出(實際上很多時候都是\(\,\Theta(1)\,\)的),
\(\,g(1)\,\)也顯然可以在\(\,\Theta(1)\,\)的時間里求出,所以只需要在低於線性的時間里求\(\,g(d)S(\frac{n}{d})\,\)的除去第一項的前綴和
考慮使用整除分塊,則轉化為求\(\,g\,\)在不超過\(\,2\sqrt n\,\)段上的和,前綴和即可,
對於\(\,S([\frac{n}{d}])\,\)項,我們直接遞歸,暴力地預處理出\(n^{\frac{2}{3}}\)個前綴和即可
復雜度分析
下文考慮中均認為求\(\,f,g\,\)的前綴和是\(\,\Theta(1)\,\)的
運用杜教篩,我們把求\(\,S(n)\,\)轉化為了求所有的\(\,S(\frac{n}{d})\,\),本質上只有\(\,2\sqrt n\,\)種取值,忽略常數后,我們設杜教篩的復雜度為\(\,T(n)\,\),可得方程
含義為,求前n項前綴和的復雜度是\(\,\)整除分塊的復雜度+處理出每個\(\,S(\frac{n}{d})\,\)和\(\,S(d)\,\)的復雜度
這里我們默認了\(\,n\,\)超出了預處理的前綴和,但\(\,\frac{n}{d}\,\)顯然可能是被預處理過的值,所以需要考慮預處理,設預處理了\(\,k\,(k\geq\sqrt n)\,\)個,那么總的復雜度為:
注意到所有\(\,T(i)\,\)都已經被預處理了,可以\(\,\Theta(1)\,\)得到,復雜度為\(\,\Theta(\sqrt n)\,\),
接着考慮\(\,T(\frac{n}{i})\,\)的處理,考慮繼續展開,注意此時不需要再預處理了:
上式最后的\(\,\sum\,\)含義為一個大於\(\,k\,\)的值迭代到\(\,k\,\)以內的迭代次數
令總復雜度為\(\,D=k+T(n)\,\),選\(\,k\,\)為主元,考慮其極值,得
最終的復雜度為\(\,\Theta(n^\frac{2}{3})\).
(5)
莫比烏斯反演
證明如下
得證
積性函數
(6)
對於任意積性函數\(f\,\)有
有算數基本定理和積性函數的定義容易推出
(7)對於任意完全積性函數\(f\,\)有
若積性函數\(f\,\)滿足上式,則\(f\,\)也是完全積性函數
(8)
積性函數的神秘性質
由算數基本定理容易推出
(9)
由結論1得:
考慮消去其中的\(\,I\,\),利用結論2,兩邊同卷\(\,\mu\):
Powerful Number 篩
個人覺得比上面兩種篩法都簡單
先定義powerful number為\(\forall n,\forall p\in P\,\and p\mid n,p^2\mid n\)
說人話就是所有質因子次數不小於2的數
還是求積性函數的前綴和,構造函數\(g,g(p)=f(p)\),並使得\(\,g\,\)的前綴和容易求出
令
有
考慮展開卷積,得
考慮\(\,g\,\)的前綴和是容易求出的,交換求和號
這依然是\(\,\Theta(n)\,\)的,但因為\(\,h\,\)是積性函數並且\(\,h(p)=0\),所以\(\,\forall h(n)\neq 0,n\in Powerful\ Number\)
考慮\(\,Powerful\ Number\,\)的數量,以下的\(\,q\,\)是\(Powerful\ Number\)
由定義知
上文的\(\,p_i\,\)僅表示\(\,q\,\)的質因子
不難發現
那么
記\(\,power(n)=[n\in Powerful\ Number]\,\),那么powerful number的數目就是\(\,power()\,\)的前綴和
考慮枚舉\(\,A\),計算對於的貢獻,為了不算重,應該只枚舉\(1-\sqrt n\),那么
那么就只需要求出\([1,n]\)中的所有powerful number,然后就可以\(\Theta(F\sqrt n)\)求出\(\,f\,\)的前綴和,其中\(\,F\,\)是求\(\,g\,\)的前綴和的復雜度
接下來介紹一個小技巧:
觀察\(f=g\,\ast\,h\),發現\(f\,\ast\,g^{-1}=\epsilon\,\ast\,h=h\),其中\(g^{-1}\,\ast\,g=\epsilon\)
所以只要構造出\(\,g\,\)就可以求出\(\,h\),減小了構造的工作量
powerful number篩的時間復雜度和思維難度都有一定優勢,但其輔助函數難以構造,使得很多問題不能用其解決
真定理
1.歐拉定理:
特殊地,\(gcd(a,p)=1\,\)時,有\(\,\,a^k\equiv\,a^{k\mod\varphi(p)}(mod\,\,p)\)
注意,使用時應當判斷\(\,k\,\)與\(\varphi (p)\)的大小
2.費馬小定理
其實就是歐拉函數的性質在歐拉定理中的應用:
代碼
封裝好了一些基本的東西
盧卡斯定理沒有過
class MyMath
{
public:
vector<int> prime;
vector<int> phi;
vector<int> mu;
template<typename Tp_> inline Tp_ inv(Tp_ x,Tp_ p) {return pow(x,-1,p);}
inline bool is_prime(ll x)
{
if(x<0) x=-x;
if(!prime.empty())
if((*prime.rbegin())*(*prime.rbegin())>=x)
{
for(vector<int>::iterator it=prime.begin();(*it)*(*it)<=x&&it!=prime.end();++it)
if(!(x%(*it))) return false;
return true;
}//surely a bit faster,almost log(n)
for(ll i(2);i*i<=x;++i)
if(!(x%i)) return false;
return true;
}//n^0.5 hardly used
inline ll pow(ll a,ll x,ll p)
{
if(x==0) return 1;
if(x>0)
{
ll ret=1;
while(x) ret=(x&1?a*ret%p:ret),x>>=1,a=a*a%p;
return ret;
}
else
{
if(!phi.empty())
if(phi.size()>p)
return pow(a,x%phi[p]+phi[p],p);
ll ph=Phi(p);
return pow(a,x%ph+ph,p);
}
}//maybe log,n^0.5 when using Phi()
template <typename Tp_> inline Tp_ gcd(Tp_ x,Tp_ y)
{
if(x>y) swap(x,y);
if(x==0) return (y?y:1);
return gcd(y%x,x);
}//almost log
template <typename Tp_> inline Tp_ lcm(Tp_ x,Tp_ y) {return x/gcd(x,y)*y;}
inline int ex_gcd(int a,int b,int c,int &p,int &q)
{
int g=ExEuclid(a,b,p,q);
if(c%g) return p=0,q=0;
g=c/g,p*=g,q*=g;
return 1;
}//a*p+b*q=c(mod p),about log,maybe
template<typename Tp_> inline Tp_ Phi(Tp_ x)
{
Tp_ tmp=x,ret=x;
for (Tp_ i=2;i*i<=x;++i)
if (tmp%i==0)
{
ret=ret-ret/i;
while (tmp%i==0) tmp/=i;
}
if(tmp>1) ret-=ret/tmp;
return ret;
}//n^0.5
template<typename Tp_> inline Tp_ Lucas(Tp_ n,Tp_ m,Tp_ p)
{
if(m==0) return 1;
Tp_ np=n%p,mp=m%p;
if(np<mp) return 0;
mp=min(np-mp,mp);
Tp_ p1=1,p2=1;
for( Tp_ i = 0 ; i < mp ; ++i )
p1=p1*(n-i)%p,
p2=p2*(i+1)%p;
return (p1*pow(p2,p-2)%p)*Lucas(n/p,m/p,p)%p;
}//p must be a prime
inline void sieve(const int capN)
{
bool *isp;
isp=new bool [capN+5];
memset(isp,0,sizeof(bool)*(capN+4));
if(!prime.empty()) prime.clear();
for(int i(2);i<=capN;++i)
{
if(!isp[i]) prime.emplace_back(i);
for(vector<int>::iterator it=prime.begin();it!=prime.end();++it)
{
if(i*(*it)>capN) break;
isp[i*(*it)]=1;
if(!(i%(*it))) break;
}
}
delete isp;
}//O(n) and need more place
inline void phi_sieve(const int capN)
{
if(!prime.empty()) prime.clear();
phi.resize(capN+4,0),phi[1]=1;
for(int i(2);i<=capN;++i)
{
if(!phi[i])
prime.emplace_back(i),
phi[i]=i-1;
for(vector<int>::iterator it=prime.begin();it!=prime.end();++it)
{
if(i*(*it)>capN) break;
if(!(i%(*it)))
{
phi[i*(*it)]=phi[i]*(*it);
break;
}
else phi[i*(*it)]=phi[i]*(*it-1);
}
}
}//cna get phi while finding primes in [1,capN]
private:
inline int ExEuclid(int a,int b,int &p,int &q)
{
if(b==0) return p=1,q=0,a;
int d=ExEuclid(b,a%b,p,q);
int tmp=p;
p=q,q=tmp-a/b*q;
return d;
}//a help function of ex_gcd
}M;