OI数论简陋入门:从入门到退役
数论笔记
前言
本文主要基本地介绍知识点,通常附有证明,可能并不太系统,顺序可(一)能(定)也有一些问题(记得看目录)
本文主要需要的前置芝士:同余,\(\,gcd,lcm\,\),乘法逆元,线性筛,剩余系,简化剩余系,导数
本文主要讲述了(不完全按顺序):(扩展)欧拉定理,费马小定理,数论函数的定义,常用积性函数及其变换,狄利克雷卷积,莫比乌斯反演,杜教筛,min_25筛,Powerful Number筛
有时间将补充:Pollard Rho,Pohlig-Hellman
本文篇幅较长,请耐心阅读,如有错误,欢迎指出
定义
一些规定
1.如无特殊标记,\(f_k(n)=f^k(n)\)
2.如无特殊说明,\(num(n,p)=max(k \in {p^k|n})\)
3.\([x]\)按照语境通常是艾弗森符号的含义或表示闭区间,有时也作向下取整(我尽量用最规范的),\(\{x\}\)表示取小数部分,有时也作集合使用
4.\(\,p\,\)表示容易质数,\(\,P\,\)表示质数集,如无特殊说明,\(\,p_i\,\)表示第\(\,i\,\)大的质数
5.\(\,lpf(x)\,\)表示\(\,x\,\)的最小质因子
6.\(\delta_m(a)\,\)表示\(\,a\,\)关于\(\,m\,\)的阶
7.使用勒让德符号,\((n|p)\equiv n^{\frac{p-1}2}(\mod p)\)
许多数论函数
素数个数函数
欧拉函数\(\,\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\)
两个积性函数卷完之后还是积性函数
上述结论均不难证明,在此略过
定理
欧拉定理&扩展欧拉定理
特殊地,\(gcd(a,p)=1\,\)时,有\(\,\,a^k\equiv\,a^{k\mod\varphi(p)}(mod\,\,p)\)
注意,使用拓展欧拉定理时应当判断\(\,k\,\)与\(\varphi (p)\)的大小
欧拉定理的证明
考虑证明简单一点的情况:
不难发现这和特殊情况是等价的(其实就是欧拉定理)
考虑\(\,p\,\)的简化剩余系(似乎也有叫缩系的)\(x_1,x_2\cdots x_{\varphi(p)}\)
构造数列\(\,y_i=ax_i\)
引理
\(\,y\,\)也是\(\,p\,\)的简化剩余系
引理证明
发现简化剩余系有两个主要性质:与原数互质,%\(p\)后两两不等
考虑分别证明
证明与原数互质
由简化剩余系的定义
再证明%\(p\)后两两不等
有
结合两条性质不难证明引理
有了引理我们不难发现,\(\,y\,\)和\(\,x\,\)只是在次序上不同
所以
得证
拓展欧拉定理的证明
同余的基本性质告诉我们
拓展欧拉定理写成如下形式
令
发现只需要证明对于每一个\(\,p_i^{q_i}\,\)拓展欧拉定理成立,对于\(\,m\,\),扩展欧拉定理就也成立
若
那么就是欧拉定理的情形,只需考虑\(\,gcd(a,p_i^{q_i})\ne1\,\)的情况
即\(\,p_i\,\)是质数,所以\(\,a=np_i\)
引理
\(\varphi(p^k)\geq k\)
考虑\(k=0,1\)时带入验证即可
对于\(k>1\)的情况,两边同时求导,即左边的导数为\(l^\prime\),右边的导数为\(r^\prime\)
得到
所以不等式在\(k>1\)时恒成立
引理得证
有
所以
于是\(\,gcd(a,p_i^{q_i})\ne1\,\)的情形也就得证了
证毕
费马小定理
其实就是欧拉函数的性质在欧拉定理中的应用:
证明略过
顺带一提,并不是所有合数都不符合费马小定理,但是费马小定理可以用来初步判断素数
中国剩余定理&扩展中国剩余定理
描述了这样一个构造方法
代入即可发现这样的 \(x\) 是一个解,于是就可以求出 \(x\mod M\) 了,可以用来求解大数取模
扩展中国定理(EXCRT)中则不需要 \(\forall i,j\ gcd(m_i,m_j)=1\) 这一条件
此时没有人类智慧的构造方法了
观察性质,记 \(LCM=lcm(m_1,m_2\dots m_n)\) 最后的答案一定是
于是考虑合并方程,直至最后剩下一个作为通解
已知
则
注意到 \(m_1,m_2,r_1,r_2\) 都已知,运用 \(ex\ gcd\) 即可求出一组 \(k_1,k_2\),代回即可求出通解
同理消去方程即可解出通解,中间任何一次合并的无解都会导致最终无解
素数定理
\(\pi(x)\sim\frac{x}{\ln x}\)
不太重要,不过证明一些复杂度时需要用到,这里给出简略的初等证明(不用\(\,\zeta\,\)函数和复变换)
考虑从离散到连续的变化,又可以写作
注意到:
\(p\mid n!\,\)当且仅当\(\,p\leq n\,\)
记
那么有
可以注意到
这似乎并不可做,但可以放缩
取对数得
用斯特林公式(或许以后会在多项式的文章里证明,不过不会也没什么关系)拟合\(\,n!\,\):
于是,我们有
记
不难发现\(\,x\geq2\,\)时,\(\frac{\\\ln x}{x-1}\)是减函数
于是
设
则
那么
有
于是
对\(\,i\,\)进行求和
因为\(\,g\,\)是减函数,且恒大于\(\,0\),所以\(\,g(p_1)-g(p_t)\,\)是收敛的
于是
两边同时求导得
于是
最后一步的变换用洛必达就可以了
证毕
拉格朗日定理
\(\text{f(x)是模p意义下的整系数n次多项式,}\)\(f(x)\equiv 0(mod\ \ p)\text{至多有n个不同解}\)
考虑归纳法
\(n=0\,\)时显然成立
若\(f(x)\equiv 0(mod\ \ p)\)有\(\,n+1\,\)个不同解,依次记为\(\,x_0,x_1,x_2...x_n\)
有
于是注意到\(\,g(x)\,\)有\(\,x_1,x_2,x_3...x_n\,\)共 \(n\) 个根,矛盾
证毕
二次探测定理
\(p\in P\and p\neq2\Rightarrow x^2\equiv 1(mod\ p)\text{的解只有}\pm1\)
原方程可化为
又有 \(p\in P\)
于是得证
小知识
向下取整小结论
其中\(\,a,b,n\,\)均为正整数
证明如下:
神秘小知识
证明如下:
构造一个二元数列\(\,(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)\)
得证
结合定义不难证得
\(\,\mu\,\)的最重要性质
说人话就是\(\,\mu\,\)是\(\,I\,\)的反函数
证明如下
若\(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\,\),简单带入可得
考虑构造函数
所以有
综上,得证
\(gcd(n,m)=1\rightarrow\varphi(nm)=\varphi(n)\varphi(m)\quad\)也就是说欧拉函数是积性函数,但不是完全积性函数
莫比乌斯反演
证明如下
得证
通常在使用时遵循如下方式
交换求和号后暴力莫反即可
原根
定义
首先定义“阶”。$ a,\(关于\),m,$的阶是
其中,\(\delta_m(a)\)表示 \(a\) 关于 \(m\) 的原根
神秘定理
一个数 \(m\) 的最小原根 \(g\leq m^\frac14\)
我不会证
一些引理
引理1
\(\gcd(a,m)=\gcd(b,m)=1\text{时}\delta_m(a)\delta_m(b)=\delta_m(ab)\Leftrightarrow \gcd(\delta_m(a),\delta_m(b))=1\)
先证,\(\delta_m(a)\delta_m(b)=\delta_m(ab)\Rightarrow \gcd(\delta_m(a),\delta_m(b))=1\)
结合定义可得
再证,\(\gcd(\delta_m(a),\delta_m(b))=1\Rightarrow \delta_m(a)\delta_m(b)=\delta_m(ab)\)
考虑如下变形
同理可得
又有
已知\(\,\gcd(\delta_m(a),\delta_m(b))=1\)
于是
即
证毕
引理2
\(\gcd(a,m)=1,\delta_m(a^k)=\frac{\delta_m(a)}{\gcd(\delta_m(a),k)}\)
给个式子的大写
代入后易得右边是 \(\delta_m(a^k)\) 的倍数
只需证明左边也是右边的倍数
有
于是
得证
引理3
\(\forall \gcd(a,m)=\gcd(b,m)=1,\exists c\ s.t. \delta_m(c)=lcm(\delta_m(a),\delta_m(b))\)
考虑如下构造(反正我考虑不到)
可以发现符合要求,得证
存在原根的判断
结论:\(m=1,2,4,p^a,2p^a\) 时才有原根
\(m=1,2,4\) 时易得证
先考虑奇素数的情况
考虑拉格朗日定理
又由费马小定理
可得
于是,\(n\,\)为\(\,p\,\)的一个原根
设 \(g\) 为 \(p\) 的一个原根,由归纳法可知
由欧拉定理得
设 \(1\leq b\leq a,\delta_{p^a}(g)=p^{b-1}(p-1)\)
考虑到
这样就得到了所有满足条件的情况,接着需要证明其他情况都无原根
\(p=2^r\) 时,\(g\) 只能为奇数
无原根
否则,\(p=rt\ \ s.t.\gcd(r,t)=1\)
无原根
综上,只有\(m=1,2,4,p^a,2p^a\) 时,\(m\) 有原根
原根的求法
根据经验(我不会证),最小原根不是多项式级别的,所以可以暴力找最小的原根
显然,如果 \(g\) 是 \(m\) 的原根,下面是充要条件
复杂度大概是\(\Theta(m^\frac14\log m\log\log m)\),\(m^\frac14\) 是最小原根的上界,\(\log m\) 是快速幂,\(\log\log m\)是枚举因子
然后有
就可以求出所有原根了,证明以后有时间再补上吧,顺便我们还可以得到 \(m\) 的原根数目为\(\varphi(\varphi(m))\)
积性函数的一些性质
通用性质
对于任意积性函数\(f\,\)有
有算数基本定理和积性函数的定义容易推出
对于任意完全积性函数\(f\,\)有
若积性函数\(f\,\)满足上式,则\(f\,\)也是完全积性函数
\(\,\mu\,\)与\(\,\varphi\,\)的关联
由结论1得:
考虑消去其中的\(\,I\,\),利用结论2,两边同卷\(\,\mu\):
一些自定义函数
\(\varphi^2\)
\(\phi^2\)
一点补充
暴力展开即可得证,读者自证不难
算法
卢卡斯定理&拓展卢卡斯定理
算法
所以为什么叫定理啊
不妨直接说 \(exLucas\) 定理,可以用来解决
\(C_n^m\mod p\)
其中,\(p\) 可以是任意正整数
不妨直接展开
如果 \(p\) 的质因子足够大,就可以直接求 \(m!(n-m)!\) 的逆元
如果 \(p\) 比较小,我们考虑
注意到
原问题就转化为了求
之后有
使用中国剩余定理求解即可得到 \(C_m^n\mod p\),复杂度为 \(\Theta(\lambda(p))\)
发现,只要 \(C_n^m\) 中没有 \(p\) 这个质因子,就可以直接求逆元
这时,一个朴素的思想是把其中的 \(p\) 都提出来,最后再乘回去,即
只需要处理 \(c_1-c_2-c_3\ne0\) 的情况
转化为处理如下情形
发现并没有很好的方法,于是考虑一次只除去一个 \(p\) ,也就是说将 \(1\sim n\) 的所有 \(p\) 的倍数都除去一个 \(p\)
注意到这样的数有 \(\lfloor\frac{n!}{p}\rfloor\) 个
除去 \(p\) 之后,有
显然可以递归直到 \(\lfloor\frac{n!}{p}\rfloor< p\)
再观察 \(n!\) 中不含 \(p\) 的项,有
于是可以发现循环节,最后一节可以不完整,但长度不超过 \(p^k\),所以这一步的复杂度是\(\Theta(p^k+\log\frac{n}{p^k})\)
因为 \(p\) 已经被提出来了,所以最后的结果就是
注意其中每一部分计算时都需要忽视 \(p\) (因为需要提出来放在外面处理)
这样就解决了问题,有一个简洁的式子,但是我忘了
复杂度
\(p\) 的质因子个数为 \(\lambda(p)\),又有 \(\Theta(\lambda(p))=\Theta(\log\log p)\)
接着分析计算 \(\frac{n!}{p^\alpha}\mod p^k\) 的复杂度
设其复杂度为 \(\Theta(f_p(n))\),则
至多递归 \(\log_pn\) 次,于是有
记
则
再全部加起来
左边不太可能是瓶颈,只考虑右边
这样放缩显然过于粗略(但确实是界,\(p=2^\alpha\) 时取到),一般估计时可以采用作为近似的下界 \(\Theta(p^{\frac1{\log\log\log p}}\frac{\log\log p}{\log p}\log n)\)
真的下界可能是 \(\Theta(p^{\frac1{\log\log p}}\frac{\log\log p}{\log p}\log n)\)
是假设有 \(\log\log p\) 个等大的因数得出的
代码
namespace Lucas
{
inline int pow(ll a,ll x,int p)
{
if(a>=p) a%=p;
if(a==1) return 1;
if(a==0) return 0;
if(a==-1) return (x&1)?-1:1;
int as=1,ds=a;
while(x)
(x&1)&&(as=(ll)as*ds%p),
ds=(ll)ds*ds%p,
x>>=1ll;
return as;
}
inline int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1,y=0;
return a;
}
int t=exgcd(b,a%b,y,x);
y-=a/b*x;
return t;
}
inline int p_num_of_fact(ll n,int p)
{
int as=0;
for(;n>=p;n/=p) as+=n/p;
return as;
}
inline int mod_fact(ll n,int p,int mod)
{
if(!n) return 1;
int as=1;
for(int i=2,md=2;i<=mod;++i,++md)
if(md==p) md=0;
else as=(ll)as*i%mod;
as=pow(as,n/mod,mod);
for(int i=2,md=2,ed=n%mod;i<=ed;++i,++md)
if(md==p) md=0;
else as=(ll)as*i%mod;
return (ll)as*mod_fact(n/p,p,mod)%mod;
}
inline int a_choose(ll n,ll m,int p,int mod)
{
if(m>n) return 0;
int c1(p_num_of_fact(n,p)),c2(p_num_of_fact(m,p)),c3(p_num_of_fact(n-m,p));
int as=1,tmp=0;
if(c1-c2-c3>0)
if((tmp=pow(p,c1-c2-c3,100000000))>=mod) return 0;
else as=tmp%mod;
else tmp=0;
int a1(mod_fact(n,p,mod)),a2(mod_fact(m,p,mod)),a3(mod_fact(n-m,p,mod));
if(as^1) a1=(ll)a1*as%mod;
int x(0),y(0);
exgcd(a2*a3%mod,mod,x,y);
return (ll)a1*x%mod;
}
inline int exLucas(ll n,ll m,int p)
{
static int a[110],b[110],cnt;
int x=p;
for(int i=2,k=sqrt(x)+1,tmp=1;i<=k;++i)
if(p%i==0)
{
for(tmp=1;x%i==0;) tmp*=i,x/=i;
a[++cnt]=tmp,b[cnt]=a_choose(n,m,i,tmp);
}
if(x^1) a[++cnt]=x,b[cnt]=a_choose(n,m,x,x);
int A=a[1],B=b[1];
for(int i=2,k1=0,k2=0;i<=cnt;++i)
{
int g=exgcd( A,a[i],k1,k2);
int t=A;
A=(ll)A*a[i]/g;
B=(ll)((ll)t*k1%A*(ll)(b[i]-B)/g%A+B)%A;
}
return (A+B)%A;
}
}
BSGS&exBSGS
\(a^x\equiv b(mod\ p)\),求 \(x\)
显然最小的答案不会超过 \(p\)(只有 \(p\) 种余数,之后一定会循环),于是可以做到 \(O(p)\)
考虑根号分治
于是可以枚举 \(k\),把对应的值插到一个hash表里,然后枚举 \(l\),判断hash表中有没有对应的值即可
于是做到 \(O(\sqrt p)\)(严格来说是 \(O(\sqrt{\varphi(p)})\))
如果 \(gcd(a,p)>1\),那么不能直接采用上面的方法,因为求不出逆元
此时需要exBSGS
显然,\(b=1\Rightarrow x=0\)
考虑其他情况,设
则
有解的必要条件是 \(\frac bg\in Z\)
最后一个式子变换得
有
故 \(a^\prime\) 在模 \(p^\prime\) 的意义下一定存在逆元,然后用 BSGS 就可以了
另一种方便的方法是直接做 BSGS,枚举 \(-l\) 即可,然后代回验证即可,不过细节挺多的
Miller-Rabin
由费马小定理
发现这样的运算是 \(O(log\ p)\) 的,但符合上式的 \(p\) 也不一定是素数,于是用二次探测定理二次验证
二次验证时注意到 \(p-1\) 一定是偶数,于是取 \(\frac p2\) 继续检测,结果只能是 \(\pm1\)
这样做,一次的错误概率是 \(\frac14\),可以接受
\(\text{如果广义黎曼猜想成立,只需取前}\lfloor2(log\ n)^2\rfloor\text{个数进行费马小定理的检验即可确定性地判素}\)
虽然我们不难知道广义黎曼猜想是否成立,但在 \(2^{78}\) 的范围内只需要取 \(2,3,5,7,11,13,17,19,23,29,31,37\) 即可确定性的判素
namespace math
{
inline ll rand_num() {return (ll)rand()*rand()*rand()+(ll)rand()*rand();}
inline ll pow(ll a,ll x,ll p)
{
if(a>=p) a%=p;
if(a==1) return 1;
if(a==0) return 0;
if(a==-1) return (x&1)?-1:1;
ll as=1,ds=a;
while(x)
(x&1)&&(as=(ll)as*ds%p),
ds=(ll)ds*ds%p,
x>>=1ll;
return as;
}
inline bool is_prime(ll n)
{
int tab[12]={2,3,5,7,11,13,17,19,23,29,31,37};
for(int i=0;i<12;++i)
if(n==tab[i]) return true;
else if(n%tab[i]==0) return false;
for(int i=0;i<12;++i)
if(pow(tab[i],n-1,n)!=1)
return false;
return true;
}
}
杜教筛
杜教筛的构造
要求\(\,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})\).
min25筛
求函数\(F(x)\)的前缀和,\(F(p^k)=pol(p^k)\),\(\,pol\,\)是多项式函数
对于部分函数,我们容易构造出杜教筛解决,但如果辅助函数不好构造或者干脆不是积性函数,就不能杜教筛了,此时,我们可以考虑min25筛(当然min_25筛对于函数的性质还是有要求的,一般不是积性函数的话,合数部分贡献的计算很好处理,下文只考虑是积性函数的情况)
计算方法
对于一个多项式,我们可以把每一项的贡献分开考虑,而常数显然可以最后再乘,所以我们只需要考虑\(F(p)=p^k\)的情况
考虑一个更简单的问题
求\(\sum\limits_{p\leq n}p^k\)
不能线筛的话似乎毫无办法,但这里可以考虑dp,记
也就是说\(\,g(n,x)\,\)表示区间\(\,[1,n]\,\)中质数或最小质因子比第\(\,x\,\)大质数大的数的\(\,k\,\)次方的贡献
怎么转移呢?
我们考虑容斥,显然答案会减小,失去贡献的是最小质因子恰好为\(\,p_j\,\)的合数,又发现\(p^k\)这种幂函数是完全积性的
所以选定一个\(p_j\)除掉,再补上多减的,可以得到转移方程:
答案就是\(\,g(n,\pi(n))\,\),可是,这还是线性的呀?
但是,我们发现
于是最后的\(g(p_{j-1},j-1)\)就可以\(\Theta(\sqrt n)\)线筛预处理了
至于前面的部分,之后再说明
回到原问题,我们把贡献拆成质数和合数两部分,然后枚举合数的\(\,lpf\,\)及其次数
设\(\,S(i,j)\,\)表示前\(\,i\,\)个数中的质数或最小质因子不小于\(\,p_j\,\)的合数的贡献
即
不难发现,边界条件为
最终答案就是
这时,我们发现我们只需要求\(\,S(n,1)\),于是我们求的\(\,g\,\)形如
可以发现只需要求\(\,\Theta(\sqrt n)\,\)个\(\,g\,\)的值就可以了
复杂度分析
我们发现min_25筛干了这些事:求\(\Theta(\sqrt n)\,\)个\(\,g\,\)的值,求\(\,\Theta(\sqrt n)\,\)个关键点的\(\,g\,\)值,求\(\,S(n,1)\,\)
第一个可以线筛,所以瓶颈在后两个上
由素数定理,质数个数约为\(\,\frac{n}{\\\ln n}\,\)
联想埃筛,可以发现一个数\(\,x\,\)被筛的次数是\(\,\Theta(\frac{\\\sqrt x}{\log \sqrt x})\,\)(此处不区分\(\,\log\,\)和\(\,\ln\,\)的区别,主要是为了和其他算法复杂度写法又一致)
于是可以得到方程:
由离散到连续变换:
可以证明左边总是不大于右边,于是
Talk is cheap,show you the code
inline void init()
{
is[1]=1;
for(int i=1;i<=q;++i)
{
if(!is[i])
p[++cnt]=i,
sp1[cnt]=(sp1[cnt-1]+i)%mod,
sp2[cnt]=(sp2[cnt-1]+1ll*i*i)%mod;
for(int j=1;j<=cnt&&p[j]*i<=q;++j)
{
is[i*p[j]]=1;
if(!(i%p[j]))break;
}
}
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
w[++tot]=n/l;
g1[tot]=w[tot]%mod;
g2[tot]=g1[tot]*(g1[tot]+1)/2%mod*(2*g1[tot]+1)%mod*inv3%mod;
g2[tot]--;
g1[tot]=g1[tot]*(g1[tot]+1)/2%mod-1;
if(n/l<=q)ind1[n/l]=tot;
else ind2[n/(n/l)]=tot;
}
}
ll k;
inline ll S(ll x,int y)
{
if(p[y]>=x)return 0ll;
k=(x<=q?ind1[x]:ind2[n/x]);
ll as=(g2[k]-g1[k]+mod-(sp2[y]-sp1[y])+mod)%mod;
for(int i=y+1;i<=cnt&&p[i]*p[i]<=x;++i)
{
ll pe=p[i],tmp;
for(int e=1;pe<=x;e++,pe=pe*p[i])
tmp=pe%mod,
as=(as+tmp*(tmp-1)%mod*(S(x/pe,i)+(e!=1)))%mod;
}
return as%mod;
}
int main()
{
read_(n),
q=sqrt(n);
init();
for(int i=1;i<=cnt;++i)
for(int j=1;j<=tot&&(ll)p[i]*p[i]<=w[j];++j)
k=(w[j]/p[i]<=q?ind1[w[j]/p[i]]:ind2[n/(w[j]/p[i])]),
g1[j]-=(ll)p[i]*(g1[k]-sp1[i-1]+mod)%mod,
g2[j]-=(ll)p[i]*p[i]%mod*(g2[k]-sp2[i-1]+mod)%mod,
g1[j]%=mod,
g2[j]%=mod,
(g1[j]<0)&&(g1[j]+=mod),
(g2[j]<0)&&(g2[j]+=mod);
write_((S(n,0)+1)%mod,'\n');
return 0;
}
模板题的代码
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筛的时间复杂度和思维难度都有一定优势,但其辅助函数难以构造,使得很多问题不能用其解决
代码
封装好了一些基本的东西
卢卡斯定理没有过
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;
感悟
就是瞎扯,主要是一些基本的思路:
即是最小的,又是最大的,就是唯一的;
先考虑是否存在,再考虑其他性质;
不知道怎么做的时候可以考虑用近似公式拟合;
日志
upd 2021.11.5更新了格式和筛法
upd 2022.2.18更新了原根
upd 2022.2.19 exLucas定理
upd 2022.3.25 EXCRT
后记
咕咕咕,咕~