【数论专题】——推式子(2019/8/21更新)

 

今天主要就是推式子。

数论函数专题。

本文会参考大量资料(或者说照搬),会注明资料来源。

https://oi-wiki.org/(定义和证明等)


前置知识:

积性函数:形如,则称为积性函数。

几个必备积性函数:

  • 欧拉函数:

  

  • 莫比乌斯函数:

  

 

  • 恒等函数:

  ,其中通常记为

  • 单位函数:

  

  • 除数函数:

  

  通常记做d(n)。

  • 常数函数:

  

几个结论:

    

      

     (推导使用):

    

    

Dirichlet卷积:对于两个数论函数f,g,两者的Dirichlet卷积为:

一些基本的卷积式子:

(图源OIwiki)

 这些式子对化简题目有极大作用。

   既然是推式子,直接从一道例题入手:

  求:

  

  如果考虑朴素做法,对于n太大的情况无从下手,那么我们来简化式子。

  我们考虑引入莫比乌斯函数:

  我们枚举(i,j)的大小:

  

    式子内同除以d,得到:

    

   引入莫比乌斯函数:

   

   将枚举顺序变换,同第一步,先枚举(i,j),就可以把第四个sigma放到前面:

   

   后面两个求和因为贡献是1,因此直接写成平方形式:

   

    这里其实就可以直接数论分块写了,但还可以继续推:

  令,枚举T的大小,并用Dirichlet卷积搞一搞:

  

    

    我们把phi用线性筛整出来,很容易就得到答案了。

来一道练手题:

 

P3327 [SDOI2015]约数个数和

最后用数论分块等东西搞搞就行了。

补充一个筛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 }

 

 


 (于8/21日更新)

抽空学了一下min_25筛,重新整理一下思路:

使用对象:

  对于一个函数,需要满足以下三点:

  • 是一个积性函数。
  • 可以用一个较简单的多项式来表达
  • 可以由快速求得

用途:求出包含以上三个要点的函数的前缀和。

  • 质数部分:

    先给个式子:

    

表示小于等于n的所有数x中,最小质因子大于Pi或x本身为质数的函数和

注意:这个函数把部分合数也当做质数处理了,但是本身不会影响答案统计,所以也是作者的明见之处。

对于这个过程:

我们去掉了Pi这个质因子,那么考虑两种情况:

  • 对于一个最小质因子Pi,它能表达的最小合数就是Pi的平方,如果,则说明n中无Pi这个质因子,因此对答案没有贡献,可以直接转移。
  • 如果包含这个最小质因子,那么就要减去所有含有质因子Pi的合数,即,但是这样又会重复减掉含有比Pi更小的质数的一些形如的数,因此还要加回来,即,组合起来就是上面那串式子了!
  • 合数部分:

    再来个式子:

    

表示小于等于n的所有x中,最小质因子大于等于Pi的函数和

(一个提醒:在质数部分中我们已经求出了的取值

这个转移看起来好复杂,实际上也很简单:

  • 对于前半部分:

  分别是小于等于n的质数贡献之和,与不满足条件的部分,(质因子小于i的部分),合起来就是所有满足条件的质数贡献之和

  • 对于后半部分:

  

  第一个sigma是枚举质因数,第二个是枚举指数,实际上整个过程就是在处理合数的情况,最后那个小部分是用来处理形如的数,他们虽然也是合数,但因子只有一个并且是质数。

   到这min_25筛的部分就基本结束了。什么?你问我板子怎么打?我也不知道。

  


 

   下一次会是什么呢?continue……

posted @ 2019-08-21 20:58  Nelson992770019  阅读(523)  评论(0编辑  收藏  举报