[关闭]
@xiaoziyao 2021-05-02T09:02:43.000000Z 字数 10988 阅读 1307

杜教筛学习笔记

数学 学习笔记


upd:内容比较老旧,但是不想更新了,到时候会写在另一个博客里。

主要是上一篇字数太多,太卡了,于是杜教筛就换了一篇写 多水了一篇博客

先讲一下前置知识:

前置知识

莫比乌斯函数

莫比乌斯函数,即

狄利克雷卷积

狄利克雷卷积,可以表示为

狄利克雷卷积满足交换律和结合律,且存在单位元

简证为单位元:

例子:

重要的数论函数中互相转化:


可以列一个表:

公式推导

  1. 莫比乌斯反演公式
  2. 莫比乌斯函数的积性证明
    使

整除分块

在莫比乌斯反演的应用题中,经常会有的形式,但是的复杂度有时候仍然满足不了需求

为了解决这一问题,我们就要用到整除分块

很容易发现很多的值是一样的,且所有的为一个不下降子序列,呈块状分布

通过简单的计算可以得到,对于每个起点为的块,它的值为,终点为,然后我们就可以用的算法计算的值了

代码:

  1. int l=1,r,ans=0;
  2. if(n>m)
  3. swp(n,m);
  4. while(l<=n){
  5. r=min(n/(n/l),mm/(m/l));
  6. ans+=(r-l+1)*(n/l);
  7. l=r+1;
  8. }

杜教筛

我们要筛一个积性函数的前缀和,杜教筛可以帮我们做到(当然可以记忆化常数优化)

我们设,其中为积性函数

我们找一个可以与卷积的积性函数,推一下它们的狄利克雷卷积前缀和:

我们可以递归求解,而且可以设一个边界,如果则直接输出预处理的前缀和

然后我们考虑的值:

此时,如果找到一个合适的使的前缀和可以很容易处理,那么就可以用数论分块递归求解

我们可以由这个表得出一些数论函数相应的函数:

即:

杜教筛应用

1.例题1:P4213 【模板】杜教筛(Sum)
题意:共组数据,每组数据给定,求

我们根据上面的表很容易写出来,因此没有什么推导过程

代码:

  1. #include<stdio.h>
  2. #include<map>
  3. using namespace std;
  4. const long long maxn=3000005;
  5. long long i,j,k,m,n,T,cnt;
  6. long long a[maxn],p[maxn],varphi[maxn],mu[maxn],sumvarphi[maxn],summu[maxn];
  7. map<long long,long long>ansvarphi,ansmu;
  8. long long getvarphi(long long n){
  9. if(n<maxn)
  10. return sumvarphi[n];
  11. if(ansvarphi.count(n))
  12. return ansvarphi[n];
  13. long long l=2,r,res=n*(n+1)/2;
  14. while(l<=n){
  15. r=n/(n/l);
  16. res-=(r-l+1)*getvarphi(n/l);
  17. l=r+1;
  18. }
  19. return ansvarphi[n]=res;
  20. }
  21. long long getmu(long long n){
  22. if(n<maxn)
  23. return summu[n];
  24. if(ansmu.count(n))
  25. return ansmu[n];
  26. long long l=2,r,res=1;
  27. while(l<=n){
  28. r=n/(n/l);
  29. res-=(r-l+1)*getmu(n/l);
  30. l=r+1;
  31. }
  32. return ansmu[n]=res;
  33. }
  34. int main(){
  35. p[1]=varphi[1]=mu[1]=1;
  36. for(i=2;i<maxn;i++){
  37. if(p[i]==0)
  38. a[++cnt]=i,varphi[i]=i-1,mu[i]=-1;
  39. for(j=1;j<=cnt;j++){
  40. if(i*a[j]>=maxn)
  41. break;
  42. p[i*a[j]]=1;
  43. if(i%a[j]==0){
  44. varphi[i*a[j]]=varphi[i]*a[j],mu[i*a[j]]=0;
  45. break;
  46. }
  47. varphi[i*a[j]]=varphi[i]*(a[j]-1),mu[i*a[j]]=-mu[i];
  48. }
  49. }
  50. for(i=1;i<maxn;i++)
  51. sumvarphi[i]=sumvarphi[i-1]+varphi[i],summu[i]=summu[i-1]+mu[i];
  52. scanf("%lld",&T);
  53. while(T--){
  54. scanf("%lld",&n);
  55. printf("%lld %lld\n",getvarphi(n),getmu(n));
  56. }
  57. return 0;
  58. }

2.例题2:简单的数学题
题意:求
数据范围:
相信大家都看了之前莫比乌斯反演的讲解,因此这里过程简单一点:


,即加到的和
因此:

用狄利克雷卷积推一下:

因此继续化简:


我们很容易看出是积性函数,因为:
由于为积性函数,若,则相乘得:

因此我们可以用杜教筛来处理的前缀和,原式化为:,此时原式可以数论分块来做
考虑如何筛,我们设另一个数论函数为,则它们的卷积为:
我们尝试用消去这个项,设,然后可以推出来:

由欧拉反演得:

因此可以用杜教筛的式子:

我们可以用小学奥数的思路推一下
首先,因此:

那么肯定也很显然了,就不推了,最终的式子为
代入杜教筛的式子得:

用数论分块和递归可以在很快的时间内用计算出
然后预处理一些就可以了
最后代入原式:

然后数论分块就结束了

代码:

  1. #include<stdio.h>
  2. #include<map>
  3. using namespace std;
  4. const long long maxn=5000005;
  5. long long i,j,k,m,n,mod,cnt,inv6;
  6. long long a[maxn],p[maxn],varphi[maxn],S[maxn];
  7. map<long long,long long>ans;
  8. inline long long getsum(long long n){
  9. return n*(n+1)/2%mod;
  10. }
  11. inline long long calc(long long n){
  12. return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
  13. }
  14. long long ksm(long long a,long long b,long long mod){
  15. long long res=1;
  16. while(b){
  17. if(b&1)
  18. res=(res*a)%mod;
  19. a=(a*a)%mod,b>>=1;
  20. }
  21. return res;
  22. }
  23. long long getS(long long n){
  24. if(n<maxn)
  25. return S[n];
  26. if(ans.count(n))
  27. return ans[n];
  28. long long l=2,r,res=getsum(n)*getsum(n)%mod;
  29. while(l<=n){
  30. r=n/(n/l);
  31. res=((res-(calc(r)-calc(l-1)+mod)%mod*getS(n/l)%mod)%mod+mod)%mod;
  32. l=r+1;
  33. }
  34. res=(res%mod+mod)%mod;
  35. return ans[n]=res;
  36. }
  37. long long getans(long long n){
  38. long long l=1,r,res=0;
  39. while(l<=n){
  40. r=n/(n/l);
  41. res=(res+(getS(r)-getS(l-1)+mod)%mod*getsum(n/l)%mod*getsum(n/l)%mod)%mod;
  42. l=r+1;
  43. }
  44. res=(res%mod+mod)%mod;
  45. return res;
  46. }
  47. int main(){
  48. scanf("%lld%lld",&mod,&n);
  49. inv6=ksm(6,mod-2,mod);
  50. p[1]=varphi[1]=1;
  51. for(i=2;i<maxn;i++){
  52. if(p[i]==0)
  53. a[++cnt]=i,varphi[i]=i-1;
  54. for(j=1;j<=cnt;j++){
  55. if(i*a[j]>=maxn)
  56. break;
  57. p[i*a[j]]=1;
  58. if(i%a[j]==0){
  59. varphi[i*a[j]]=varphi[i]*a[j];
  60. break;
  61. }
  62. varphi[i*a[j]]=varphi[i]*(a[j]-1);
  63. }
  64. }
  65. for(i=1;i<maxn;i++)
  66. S[i]=(S[i-1]+varphi[i]%mod*i%mod*i%mod)%mod;
  67. printf("%lld\n",getans(n));
  68. return 0;
  69. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注