zoukankan      html  css  js  c++  java
  • 一些重要的数论函数+线性筛模板

    数论这块其实有好多函数,挺让人头疼的。

    本篇主要讲解一些数论函数(欧拉函数、约数个数、约数和、莫比乌斯函数)以及它们的线性筛求法。

    1.欧拉函数 φ(n)

    欧拉函数φ(n)表示比n小的、且与n互质的正整数的数量。

    如果把n分解成若干质因数之积的形式:n=(p1^a1)*(p2^a2)*...*(pk^ak)的话:

    φ(n)=n*(1-1/p1)*(1-1/p2)*...*(1-1/pk)。

    显然把不互质的都减掉,剩下的就是互质的。

    这个可以通过分解质因数在O(sqrt(n))的时间内算出来。

    线性筛法:

    代码里我用phi表示欧拉函数。

    计算的时候只需记录phi[n]即可。

    可以把n*(1-1/p1)*(1-1/p2)*...*(1-1/pk)里的n拆成(p1^a1)*(p2^a2)*...*(pk^ak)分别乘进括号里。

    这么说如果某质数p只有一次幂,贡献就是p-1(n拆分出来的一个p进括号,和1-1/p相乘后的结果)。

    否则(p出现了多次),除了第一次是p-1,剩下的几次都是p(1-1/p已经用过了,没有东西能跟p相乘约掉了)。

    显然i%pr[j]!=0的时候代表(i*pr[j])这个数中pr[j]只有一次幂。这样就能判断某个p是否是第一次出现了。

     1 phi[1]=1;
     2 for(int i=2;i<=n;i++)
     3 {
     4     if(!vis[i])
     5     {
     6         phi[i]=i-1;
     7         pr[++pc]=i;
     8     }
     9     for(int j=1;j<=pc&&i*pr[j]<=n;j++)
    10     {
    11         vis[i*pr[j]]=1;
    12         if(i%pr[j])
    13         {
    14             phi[i*pr[j]]=phi[i]*(pr[j]-1);
    15         }else
    16         {
    17             phi[i*pr[j]]=phi[i]*pr[j];
    18             break;
    19         }
    20     }
    21 }

    2.约数个数 d(n)

    n=(p1^a1)*(p2^a2)*...*(pk^ak)

    则n的约数个数d(n)=(a1+1)*(a2+1)*...*(ak+1)。

    对于某个质因数pi,某个约数要么包含少于ai个pi,要么不包含pi。

    所以每对括号内,"1"表示如果不包含pi则只有一种选法(即不选pi)。

    而ai表示如果选pi,有ai种选法(选1个、选2个、选3个......选ai个,共ai种选法)。

    所以把每对括号乘到一起就是选法总数。每个选法都能乘出一个约数,所以选法总数就是约数个数。

    这个式子其实有点类似母函数(生成函数)。

    考虑怎么线性筛出来:

    开一个辅助数组记录最小因子的幂次,即ai。

    当i和pr[j]互质的时候,i的任意因数和pr[j]的任意因数相乘都能组成i*pr[j]的一个因数。

    当i和pr[j]不互质的时候,我们算到i的时候,认为某个因子只有a个,而到i*pr[j]这个数的时候,有a+1个了。

    所以我们把d[i]中的(a+1)除掉,再乘以(a+2)即可。

     1 d[1]=1;
     2 for(int i=2;i<=n;i++)
     3 {
     4     if(!vis[i])
     5     {
     6         d[i]=2;
     7         exp[i]=1;
     8         pr[++pc]=i;
     9     }
    10     for(int j=1;j<=pc&&i*pr[j]<=n;j++)
    11     {
    12         vis[i*pr[j]]=1;
    13         if(i%pr[j])
    14         {
    15             d[i*pr[j]]=d[i]*d[pr[j]];
    16             exp[i*pr[j]]=1;
    17         }else
    18         {
    19             d[i*pr[j]]=d[i]/(exp[i]+1)*(exp[i]+2);
    20             exp[i*pr[j]]=exp[i]+1;
    21             break;
    22         }
    23     }
    24 }

    3.约数和 σ(n)

    σ(n)表示n的所有约数之和。

    若把n分解为n=(p1^a1)*(p2^a2)*...*(pk^ak),

    则σ(n)=(1+p1+p1^2+p1^3+...+p1^a1)*(1+p2+p2^2+p2^3+...+p2^a2)*...*(1+pk+pk^2+pk^3+...+pk^ak)。

    这个式子的证明也很显然,跟约数个数的那个很像,也是类似母函数的想法,比较简单。

    这个式子括号都拆开之后的每一项都是n的一个因数嘛,当然就是要加到一起喽。

    线性筛出来稍稍有些麻烦。

    代码中我用sig(sigma太长了,我看着别扭...)表示约数和。

    开两个数组pw和spw辅助计算,pw相当于上式的pi^ai,即pi的最高次幂的值。

    spw相当于(1+pi+pi^2+...+pi^ai)。

    其它的想法就都跟约数个数差不多了。

     1 sig[1]=1;
     2 for(int i=2;i<=n;i++)
     3 {
     4     if(!vis[i])
     5     {
     6         pw[i]=i;
     7         spw[i]=i+1;
     8         sig[i]=i+1;
     9         pr[++pc]=i;
    10     }
    11     for(int j=1;j<=pc&&i*pr[j]<=n;j++)
    12     {
    13         vis[i*pr[j]]=1;
    14         if(i%pr[j])
    15         {
    16             pw[i*pr[j]]=pr[j];
    17             spw[i*pr[j]]=pr[j]+1;
    18             sig[i*pr[j]]=sig[i]*sig[pr[j]];
    19         }else
    20         {
    21             pw[i*pr[j]]=pw[i]*pr[j];
    22             spw[i*pr[j]]=spw[i]+pw[i]*pr[j];
    23             sig[i*pr[j]]=sig[i]/spw[i]*spw[i*pr[j]];
    24             break;
    25         }
    26     }
    27 }

    4.莫比乌斯函数 μ(n)

    这个基本上做莫比乌斯反演的时候用的很多......

    然而pengzhou讲完了莫比乌斯反演之后我还没怎么做那个的题......

    下面我用mu(n)表示莫比乌斯函数μ(n)。

    莫比乌斯函数,mu(1)=1。

    n存在平方因子时,mu(n)=0。

    其余情况:即n=p1*p2*p3*...*pk(其中pi为质数)时,mu(n)=(-1)^k。

    即当n不等于1、又没有平方因子时,根据n的质因子个数的奇偶性判断,是奇数则mu(n)为-1、是偶数则mu(n)为1。

    线性筛的时候很简单,质数的mu为-1,遇到平方因子则为0。不是平方因子的,反转正负性即可。

     1 mu[1]=1;
     2 for(int i=2;i<=n;i++)
     3 {
     4     if(!vis[i])
     5     {
     6         mu[i]=-1;
     7         pr[++pc]=i;
     8     }
     9     for(int j=1;j<=pc&&i*pr[j]<=n;j++)
    10     {
    11         vis[i*pr[j]]=1;
    12         if(i%pr[j])
    13         {
    14             mu[i*pr[j]]=-mu[i];
    15         }else
    16         {
    17             mu[i*pr[j]]=0;
    18             break;
    19         }
    20     }
    21 }

    最后放个四合一(虽然可能永远也用不到四合一):

     1 phi[1]=d[1]=sig[1]=mu[1]=1;
     2 for(int i=2;i<=n;i++)
     3 {
     4     if(!vis[i])
     5     {
     6         phi[i]=i-1;
     7         d[i]=2;
     8         exp[i]=1;
     9         pw[i]=i;
    10         spw[i]=i+1;
    11         sig[i]=i+1;
    12         mu[i]=-1;
    13         pr[++pc]=i;
    14     }
    15     for(int j=1;j<=pc&&i*pr[j]<=n;j++)
    16     {
    17         vis[i*pr[j]]=1;
    18         if(i%pr[j])
    19         {
    20             phi[i*pr[j]]=phi[i]*(pr[j]-1);
    21             d[i*pr[j]]=d[i]*d[pr[j]];
    22             exp[i*pr[j]]=1;
    23             pw[i*pr[j]]=pr[j];
    24             spw[i*pr[j]]=pr[j]+1;
    25             sig[i*pr[j]]=sig[i]*sig[pr[j]];
    26             mu[i*pr[j]]=-mu[i];
    27         }else
    28         {
    29             phi[i*pr[j]]=phi[i]*pr[j];
    30             d[i*pr[j]]=d[i]/(exp[i]+1)*(exp[i]+2);
    31             exp[i*pr[j]]=exp[i]+1;
    32             pw[i*pr[j]]=pw[i]*pr[j];
    33             spw[i*pr[j]]=spw[i]+pw[i]*pr[j];
    34             sig[i*pr[j]]=sig[i]/spw[i]*spw[i*pr[j]];
    35             mu[i*pr[j]]=0;
    36             break;
    37         }
    38     }
    39 }
  • 相关阅读:
    VC2005从开发MFC ActiveX ocx控件到发布到.net网站的全部过程
    【Demo 0025】注册/反注册窗体类RegisterClassEx/UnregisterClass
    《白手起家Win32SDK应用程序》(完整版+目录)
    关于初始化C++类成员
    TCP和UDP的"保护消息边界" (经典)
    (经典)tcp粘包分析
    解决TCP网络传输“粘包”问题
    无锁队列
    MFC全局函数开局——AfxGetApp解剖
    Tomcat系列之服务器的安装与配置以及各组件详解
  • 原文地址:https://www.cnblogs.com/eternhope/p/9885357.html
Copyright © 2011-2022 走看看