今天主要就是推式子。
数论函数专题。
本文会参考大量资料(或者说照搬),会注明资料来源。
https://oi-wiki.org/(定义和证明等)
前置知识:
积性函数:形如,则称为积性函数。
几个必备积性函数:
- 欧拉函数:
- 莫比乌斯函数:
- 恒等函数:
,其中通常记为
- 单位函数:
- 除数函数:
通常记做d(n)。
- 常数函数:
几个结论:
(推导使用):
Dirichlet卷积:对于两个数论函数f,g,两者的Dirichlet卷积为:
一些基本的卷积式子:
(图源OIwiki)
这些式子对化简题目有极大作用。
既然是推式子,直接从一道例题入手:
求:
如果考虑朴素做法,对于n太大的情况无从下手,那么我们来简化式子。
我们考虑引入莫比乌斯函数:
我们枚举(i,j)的大小:
式子内同除以d,得到:
引入莫比乌斯函数:
将枚举顺序变换,同第一步,先枚举(i,j),就可以把第四个sigma放到前面:
后面两个求和因为贡献是1,因此直接写成平方形式:
这里其实就可以直接数论分块写了,但还可以继续推:
令,枚举T的大小,并用Dirichlet卷积搞一搞:
我们把phi用线性筛整出来,很容易就得到答案了。
来一道练手题:
最后用数论分块等东西搞搞就行了。
补充一个筛mu函数的模板:
1 int vis[N],prime[N],mu[N],cnt; 2 void mu_sieve(){ 3 mu[1]=1; 4 for(int i=2;i<=N;i++){ 5 if(!vis[i]){ 6 prime[++cnt]=i; 7 mu[i]=-1; 8 } 9 for(int j=1;j<=cnt;j++){ 10 if(i*prime[j]>N) break; 11 vis[i*prime[j]]=1; 12 if(i%prime[j]==0){ 13 mu[i*prime[j]]=0; 14 break; 15 } 16 else mu[i*prime[j]]=-mu[i]; 17 } 18 } 19 }
在本题中,还需要筛出1~n的每个数约数个数和,可以看一下这位大佬的博客:GO
在线性筛基础上改造即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=5e4+10; 4 int t,n,m; 5 int vis[N],prime[N],mu[N],d[N],num[N],cnt; 6 //vis 标记 prime 素数 mu 莫比乌斯函数 d 约数个数 num 最小质因子个数 7 void sieve(){ 8 vis[1]=d[1]=mu[1]=1; 9 for(int i=2;i<=N;i++){ 10 if(!vis[i]){ 11 prime[++cnt]=i; 12 num[i]=1; 13 d[i]=2; 14 mu[i]=-1; 15 } 16 for(int j=1;j<=cnt&&i*prime[j]<=N;j++){ 17 vis[i*prime[j]]=1; 18 if(i%prime[j]==0){ 19 mu[i*prime[j]]=0; 20 d[i*prime[j]]=d[i]/(num[i]+1)*(num[i]+2); 21 num[i*prime[j]]=num[i]+1; 22 break; 23 } 24 else{ 25 d[i*prime[j]]=d[i]*d[prime[j]]; 26 num[i*prime[j]]=1; 27 mu[i*prime[j]]=-mu[i]; 28 } 29 } 30 } 31 for(int i=1;i<=N;i++){ 32 mu[i]+=mu[i-1]; 33 d[i]+=d[i-1]; 34 } 35 36 } 37 int main(){ 38 sieve(); 39 scanf("%d",&t); 40 for(int k=1;k<=t;k++){ 41 scanf("%d%d",&n,&m); 42 if(n>m) swap(n,m); 43 long long ans=0; 44 int i=1,j; 45 while(i<=n){ 46 j=min(n/(n/i),m/(m/i)); 47 ans+=1ll*(mu[j]-mu[i-1])*d[n/i]*d[m/i]; 48 i=j+1; 49 } 50 printf("%lld\n",ans); 51 } 52 return 0; 53 }
下面讲各种筛,continue……