[关闭]
@xiaoziyao 2020-10-18T15:36:37.000000Z 字数 27560 阅读 2268

狄利克雷卷积&莫比乌斯反演学习笔记

数学 学习笔记


前置知识

莫比乌斯函数

莫比乌斯函数,即

狄利克雷卷积

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

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

简证为单位元:

例子:

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


可以列一个表:

公式推导

  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.求值:

证明如下:
由性质5得

不妨设,则由显然可得
则有

然后后面的式子用整除分块可以优化到的时间复杂度
2.例题1:[POI2007]ZAP-Queries
题意:共组数据,求值
数据范围:
简证:原式很显然等于

然后由应用1得

然后直接整除分块,时间复杂度:

  1. #include<stdio.h>
  2. const int maxn=100005;
  3. int i,j,k,m,n,T,cnt,l,r,ans;
  4. int a[maxn],p[maxn],mu[maxn],sum[maxn];
  5. inline void swp(int &a,int &b){
  6. a+=b,b=a-b,a-=b;
  7. }
  8. inline int min(int a,int b){
  9. return a<b? a:b;
  10. }
  11. int main(){
  12. p[1]=mu[1]=1;
  13. for(i=2;i<maxn;i++){
  14. if(p[i]==0)
  15. a[++cnt]=i,mu[i]=-1;
  16. for(j=1;j<=cnt;j++){
  17. if(i*a[j]>=maxn)
  18. break;
  19. p[i*a[j]]=1;
  20. if(i%a[j]==0)
  21. mu[i*a[j]]=0;
  22. else mu[i*a[j]]=-mu[i];
  23. }
  24. }
  25. for(i=1;i<maxn;i++)
  26. sum[i]=sum[i-1]+mu[i];
  27. scanf("%d",&T);
  28. while(T--){
  29. scanf("%d%d%d",&n,&m,&k);
  30. n/=k,m/=k;
  31. if(n>m)
  32. swp(n,m);
  33. l=1,ans=0;
  34. while(l<=n){
  35. r=min(n/(n/l),m/(m/l));
  36. ans+=(sum[r]-sum[l-1])*(n/l)*(m/l);
  37. l=r+1;
  38. }
  39. printf("%d\n",ans);
  40. }
  41. return 0;
  42. }

3.例题2:[HAOI2011]Problem b
题意:共组数据,求值
数据范围:
这道题与例题1很像,但是不是从开始的,因此这有点像在一个二维平面内取一个矩形,你想到了什么?容斥定理!
我们只需要写一个跟例题1一样的函数求值
二维容斥:

  1. #include<stdio.h>
  2. const int maxn=100005;
  3. int i,j,k,m,n,a,b,c,d,T,cnt;
  4. int z[maxn],p[maxn],mu[maxn],sum[maxn];
  5. inline void swp(int &a,int &b){
  6. a+=b,b=a-b,a-=b;
  7. }
  8. inline int min(int a,int b){
  9. return a<b? a:b;
  10. }
  11. int calc(int n,int m,int k){
  12. int l=1,r,res=0;
  13. n/=k,m/=k;
  14. if(n>m)
  15. swp(n,m);
  16. while(l<=n){
  17. r=min(n/(n/l),m/(m/l));
  18. res+=(sum[r]-sum[l-1])*(n/l)*(m/l);
  19. l=r+1;
  20. }
  21. return res;
  22. }
  23. int main(){
  24. p[1]=mu[1]=1;
  25. for(i=2;i<maxn;i++){
  26. if(p[i]==0)
  27. z[++cnt]=i,mu[i]=-1;
  28. for(j=1;j<=cnt;j++){
  29. if(i*z[j]>=maxn)
  30. break;
  31. p[i*z[j]]=1;
  32. if(i%z[j]==0)
  33. mu[i*z[j]]=0;
  34. else mu[i*z[j]]=-mu[i];
  35. }
  36. }
  37. for(i=1;i<maxn;i++)
  38. sum[i]=sum[i-1]+mu[i];
  39. scanf("%d",&n);
  40. for(i=1;i<=n;i++){
  41. scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
  42. printf("%d\n",calc(b,d,k)-calc(b,c-1,k)-calc(a-1,d,k)+calc(a-1,c-1,k));
  43. }
  44. return 0;
  45. }

4.例题3:YY的GCD
题意:共组数据,求
数据范围:
不妨设,很自然可以想到枚举质数的套路:


由应用2得原式等于

但是复杂度还是太高了,我们都知道

也就是说我们每次求解的复杂度都为,当时,,再加上多组数据,是肯定无法通过的
于是我们要考虑优化,令,则有:

然后可以枚举

,则原式等于

此时可以用整除分块做到的复杂度
那么我们的目标就是将用线性筛处理,并求出前缀和,我们可以分类讨论一下:
,则
否则设,且的最小质因子,此时有:
,即的质因子出现了多次时
- 当不存在质数使时,只有,其余均存在,即
- 当存在质数使时,且其余的满足,故所有的
,即的最小质因子只出现一次时

得对于所有的质数,都有
即对于满足的质数

综上所述得

可以用线性筛在用的复杂度求出
求出之后别忘了前缀和
最终复杂度:
代码:

  1. #include<stdio.h>
  2. const int maxn=10000005;
  3. int i,j,k,m,n,cnt,T,l,r;
  4. long long ans;
  5. int a[maxn],p[maxn],mu[maxn],f[maxn],sum[maxn];
  6. inline void swp(int &a,int &b){
  7. a+=b,b=a-b,a-=b;
  8. }
  9. inline int min(int a,int b){
  10. return a<b? a:b;
  11. }
  12. int main(){
  13. p[1]=mu[1]=1;
  14. for(i=2;i<maxn;i++){
  15. if(p[i]==0)
  16. a[++cnt]=i,mu[i]=-1,f[i]=1;
  17. for(j=1;j<=cnt;j++){
  18. if(i*a[j]>maxn)
  19. break;
  20. p[i*a[j]]=1;
  21. if(i%a[j]==0){
  22. mu[i*a[j]]=0;
  23. f[i*a[j]]=mu[i];
  24. break;
  25. }
  26. mu[i*a[j]]=-mu[i];
  27. f[i*a[j]]=mu[i]-f[i];
  28. }
  29. sum[i]=sum[i-1]+f[i];
  30. }
  31. scanf("%d",&T);
  32. while(T--){
  33. scanf("%d%d",&n,&m);
  34. if(n>m)
  35. swp(n,m);
  36. l=1,ans=0;
  37. while(l<=n){
  38. r=min(n/(n/l),m/(m/l));
  39. ans+=(long long)(n/l)*(long long)(m/l)*(long long)(sum[r]-sum[l-1]);
  40. l=r+1;
  41. }
  42. printf("%lld\n",ans);
  43. }
  44. return 0;
  45. }

5.求值


不妨设,然后套路地除一个,得:

这里的原因是的范围都缩小了,故都要乘
然后将替换,得:

然后仍然是套路地将提前,并提出,得:

然后就发现这是等差数列,因此有:

最后整除分块,时间复杂度
6.例题4:[SDOI2015]约数个数和
题意:共组数据,求
数据范围:
先证明一个毒瘤式子

因为的因数个数,因此考虑将的每个因数对应一组互质的因数
对于每个质数,且的幂次为的幂次为
的幂次为,根据乘法原理,得,即贡献为
因为,则的幂次不超过的幂次不超过,且中必有一个为
- 当时,,即有种取值
- 当时,,即有种取值
- 当时,有种取值
根据加法原理得每个质数会对右边的式子贡献的取值,也可写作
故每个质数作为因子对左边的式子贡献,作为互质的数对右边的式子同样贡献,即的每个因数对应一组互质的因数
然后原式可以化为
然后用莫比乌斯函数替换得:

提前,得:

然后枚举得:

,则有:

于是可以设,则原式可化为
我们可以用处理,然后再看,发现也是一个整除分块,需要来预处理,然后的部分可以整除分块来处理
时间复杂度:
代码:

  1. #include<stdio.h>
  2. const int maxn=50005;
  3. int i,j,k,m,n,T,cnt,l,r;
  4. long long ans;
  5. int a[maxn],p[maxn],mu[maxn],sum[maxn];
  6. long long f[maxn];
  7. inline void swp(int &a,int &b){
  8. a+=b,b=a-b,a-=b;
  9. }
  10. inline int min(int a,int b){
  11. return a<b? a:b;
  12. }
  13. int main(){
  14. p[1]=mu[1]=1;
  15. for(i=2;i<maxn;i++){
  16. if(p[i]==0)
  17. a[++cnt]=i,mu[i]=-1;
  18. for(j=1;j<=cnt;j++){
  19. if(i*a[j]>=maxn)
  20. break;
  21. p[i*a[j]]=1;
  22. if(i%a[j]==0){
  23. mu[i*a[j]]=0;
  24. break;
  25. }
  26. mu[i*a[j]]=-mu[i];
  27. }
  28. }
  29. for(i=1;i<maxn;i++)
  30. sum[i]=sum[i-1]+mu[i];
  31. for(i=1;i<maxn;i++){
  32. l=1,ans=0;
  33. while(l<=i){
  34. r=i/(i/l);
  35. ans+=(r-l+1)*(i/l);
  36. l=r+1;
  37. }
  38. f[i]=ans;
  39. }
  40. scanf("%d",&T);
  41. while(T--){
  42. scanf("%d%d",&n,&m);
  43. if(n>m)
  44. swp(n,m);
  45. l=1,ans=0;
  46. while(l<=n){
  47. r=min(n/(n/l),m/(m/l));
  48. ans+=(long long)(sum[r]-sum[l-1])*(long long)f[n/l]*(long long)f[m/l];
  49. l=r+1;
  50. }
  51. printf("%lld\n",ans);
  52. }
  53. return 0;
  54. }

7.例题5:[国家集训队]Crash的数字表格 / JZPTAB
题意:求
数据范围:


,则等价于,可得:

枚举可得:

这里的原因是的范围都缩小了
替换为

,则原式为
带入应用5则有:

我们求就只需要处理的前缀和然后数论分块就可以了!
原式也是一次数论分块
代码:

  1. #include<stdio.h>
  2. const int maxn=10000005;
  3. const long long mod=20101009;
  4. long long i,j,k,m,n,cnt,l,r,ans;
  5. long long a[maxn],p[maxn],mu[maxn],sum[maxn];
  6. inline void swp(long long &a,long long &b){
  7. a+=b,b=a-b,a-=b;
  8. }
  9. inline long long min(long long a,long long b){
  10. return a<b? a:b;
  11. }
  12. inline long long calc(long long x,long long y){
  13. return (x*(x+1)/2%mod)*(y*(y+1)/2%mod)%mod;
  14. }
  15. long long f(long long n,long long m){
  16. if(n>m)
  17. swp(n,m);
  18. long long l=1,r,res=0;
  19. while(l<=n){
  20. r=min(n/(n/l),m/(m/l));
  21. res=(res+(sum[r]-sum[l-1]+mod)%mod*calc(n/l,m/l))%mod;
  22. l=r+1;
  23. }
  24. return res;
  25. }
  26. int main(){
  27. p[1]=mu[1]=1;
  28. for(i=2;i<maxn;i++){
  29. if(p[i]==0)
  30. a[++cnt]=i,mu[i]=-1;
  31. for(j=1;j<=cnt;j++){
  32. if(i*a[j]>=maxn)
  33. break;
  34. p[i*a[j]]=1;
  35. if(i%a[j]==0){
  36. mu[i*a[j]]=0;
  37. break;
  38. }
  39. mu[i*a[j]]=-mu[i];
  40. }
  41. }
  42. for(i=1;i<maxn;i++)
  43. sum[i]=(sum[i-1]+mu[i]*i*i%mod)%mod;
  44. scanf("%lld%lld",&n,&m);
  45. if(n>m)
  46. swp(n,m);
  47. l=1;
  48. while(l<=n){
  49. r=min(n/(n/l),m/(m/l));
  50. ans=(ans+((l+r)*(r-l+1)/2)%mod*f(n/l,m/l)%mod)%mod;
  51. l=r+1;
  52. }
  53. printf("%lld\n",ans);
  54. return 0;
  55. }

8.例题6:[SDOI2017]数字表格
题意:求
不妨设则:
首先套路地枚举


然后仍然套路地用莫比乌斯函数替换

然后套路地令,则原式为:
,就可以在外面直接预处理掉,因为复杂度太高所以考虑每个点更新它的倍数
最后原式为,直接数论分块搞掉
代码:

  1. #include<stdio.h>
  2. const int maxn=1000005;
  3. const long long mod=1000000007;
  4. int i,j,k,m,n,cnt,T,l,r;
  5. long long ans;
  6. int a[maxn],p[maxn],mu[maxn];
  7. long long f[maxn],g[maxn],mul[maxn];
  8. inline void swp(int &a,int &b){
  9. a+=b,b=a-b,a-=b;
  10. }
  11. inline int min(int a,int b){
  12. return a<b? a:b;
  13. }
  14. long long ksm(long long a,int b){
  15. long long res=1;
  16. while(b>0){
  17. if(b&1)
  18. res=(res*a)%mod;
  19. a=(a*a)%mod,b>>=1;
  20. }
  21. return res;
  22. }
  23. long long inv(long long a,long long b){
  24. return ksm(a,b-2);
  25. }
  26. int main(){
  27. p[1]=mu[1]=1;
  28. f[0]=0,f[1]=g[1]=1;
  29. for(i=2;i<maxn;i++){
  30. f[i]=(f[i-1]+f[i-2])%mod;
  31. g[i]=inv(f[i],mod);
  32. if(p[i]==0)
  33. a[++cnt]=i,mu[i]=-1;
  34. for(j=1;j<=cnt;j++){
  35. if(i*a[j]>=maxn)
  36. break;
  37. p[i*a[j]]=1;
  38. if(i%a[j]==0){
  39. mu[i*a[j]]=0;
  40. break;
  41. }
  42. mu[i*a[j]]=-mu[i];
  43. }
  44. }
  45. for(i=0;i<maxn;i++)
  46. mul[i]=1;
  47. for(i=1;i<maxn;i++){
  48. if(mu[i]==0)
  49. continue;
  50. for(j=i;j<maxn;j+=i){
  51. if(mu[i]==1)
  52. mul[j]=mul[j]*f[j/i]%mod;
  53. if(mu[i]==-1)
  54. mul[j]=mul[j]*g[j/i]%mod;
  55. }
  56. }
  57. for(i=2;i<maxn;i++)
  58. mul[i]=mul[i]*mul[i-1]%mod;
  59. scanf("%d",&T);
  60. while(T--){
  61. scanf("%d%d",&n,&m);
  62. if(n>m)
  63. swp(n,m);
  64. l=1,ans=1;
  65. while(l<=n){
  66. r=min(n/(n/l),m/(m/l));
  67. ans=(ans*ksm(mul[r]*inv(mul[l-1],mod)%mod,(long long)(n/l)*(long long)(m/l)%(mod-1)))%mod;
  68. l=r+1;
  69. }
  70. printf("%lld\n",(ans+mod)%mod);
  71. }
  72. return 0;
  73. }

9.例题7:[MtOI2019]幽灵乐团
我有一个绝妙的想法,但是这里空白太小,写不下
题解:[MtOI2019]幽灵乐团解题报告

  1. #include<stdio.h>
  2. const int maxn=100005;
  3. long long i,j,k,m,n,T,p,A,B,C,l,r,cnt;
  4. long long a[maxn],c[maxn],mu[maxn],varphi[maxn],fac1[maxn],fac2[maxn],w1[maxn],f1[maxn],g1[maxn],w2[maxn],f2[maxn],g2[maxn],w3[maxn],f3[maxn],g3[maxn],sum[maxn];
  5. //a[i]={Prime},c[i]=[i\in{Prime}?]
  6. //mu[i]=(i==1?)1:((i=\prod_{j=1}^s p_i,(p_x!=p_y(x!=y)))? 0:(-1)^s)
  7. //fac1[i]=i!,fac2[i]=\prod_{j=1}^i i^i
  8. //w1[i]=\prod_{d|i}i^{\mu(d/i)},f1[i]=\prod_{j=1}^i w1[i],g1[i]=f1[i]^{-1}
  9. //w2[i]=w1[i]*(i^2),f2[i]=\prod_{j=1}^i w2[i],g2[i]=f2[i]^{-1}
  10. //w3[i]=i^{\varphi(i)},f3[i]=\prod_{j=1]^i w3[i],g3[i]=f3[i]^{-1}
  11. //sum[i]=\sum_{j=1}^i varphi[i]
  12. inline long long min(long long a,long long b){
  13. return a<b? a:b;
  14. }
  15. long long ksm(long long a,long long b,long long p){
  16. long long res=1;
  17. while(b>0){
  18. if(b&1)
  19. res=(res*a)%p;
  20. a=(a*a)%p,b>>=1;
  21. }
  22. return res;
  23. }
  24. long long inv(long long a,long long b){
  25. return ksm(a,b-2,b);
  26. }
  27. void init(){
  28. c[1]=mu[1]=varphi[1]=1;
  29. for(long long i=2;i<maxn;i++){
  30. if(c[i]==0)
  31. a[++cnt]=i,mu[i]=-1,varphi[i]=i-1;
  32. for(long long j=1;j<=cnt;j++){
  33. if(i*a[j]>=maxn)
  34. break;
  35. c[i*a[j]]=1;
  36. if(i%a[j]==0){
  37. mu[i*a[j]]=0;
  38. varphi[i*a[j]]=varphi[i]*a[j];
  39. break;
  40. }
  41. mu[i*a[j]]=-mu[i];
  42. varphi[i*a[j]]=varphi[i]*(a[j]-1);
  43. }
  44. }
  45. fac1[0]=1;
  46. for(long long i=1;i<maxn;i++)
  47. fac1[i]=fac1[i-1]*i%p;
  48. fac2[0]=1;
  49. for(long long i=1;i<maxn;i++)
  50. fac2[i]=fac2[i-1]*ksm(i,i%(p-1),p)%p;
  51. for(long long i=0;i<maxn;i++)
  52. w1[i]=w2[i]=1;
  53. for(long long i=1;i<maxn;i++){
  54. if(mu[i]==0)
  55. continue;
  56. for(long long j=i;j<maxn;j+=i){
  57. if(mu[i]==1)
  58. w1[j]=(w1[j]*(j/i))%p;
  59. if(mu[i]==-1)
  60. w1[j]=(w1[j]*inv(j/i,p))%p;
  61. }
  62. }
  63. f1[0]=g1[0]=1;
  64. for(long long i=1;i<maxn;i++)
  65. f1[i]=f1[i-1]*w1[i]%p,g1[i]=inv(f1[i],p);
  66. for(long long i=1;i<maxn;i++)
  67. w2[i]=ksm(w1[i],i*i%(p-1),p);
  68. f2[0]=g2[0]=1;
  69. for(long long i=1;i<maxn;i++)
  70. f2[i]=f2[i-1]*w2[i]%p,g2[i]=inv(f2[i],p);
  71. for(long long i=1;i<maxn;i++)
  72. w3[i]=ksm(i,varphi[i],p);
  73. f3[0]=g3[0]=1;
  74. for(long long i=1;i<maxn;i++)
  75. f3[i]=f3[i-1]*w3[i]%p,g3[i]=inv(f3[i],p);
  76. for(long long i=1;i<maxn;i++)
  77. sum[i]=sum[i-1]+varphi[i];
  78. }
  79. inline long long getsum(long long x,long long p){
  80. return x*(x+1)/2%p;
  81. }
  82. long long getnmt(long long A,long long B,long long C,long long t){
  83. if(t==0){
  84. long long res=ksm(fac1[A],B*C%(p-1),p);
  85. return res;
  86. }
  87. if(t==1){
  88. long long res=ksm(fac2[A],getsum(B,p-1)*getsum(C,p-1)%(p-1),p);
  89. return res;
  90. }
  91. if(t==2){
  92. long long l=1,r,res=1;
  93. while(l<=min(min(A,B),C)){
  94. r=min(min(A/(A/l),B/(B/l)),C/(C/l));
  95. res=res*ksm(f3[r]*g3[l-1]%p,(A/l)*(B/l)%(p-1)*(C/l)%(p-1),p)%p*ksm(fac1[A/l],(sum[r]-sum[l-1]+(p-1))%(p-1)*(B/l)%(p-1)*(C/l)%(p-1),p)%p;
  96. l=r+1;
  97. }
  98. return res;
  99. }
  100. }
  101. long long getres(long long A,long long B){
  102. long long l=1,r,res=1;
  103. while(l<=min(A,B)){
  104. r=min(A/(A/l),B/(B/l));
  105. res=res*ksm(f1[r]*g1[l-1]%p,(A/l)*(B/l)%(p-1),p)%p;
  106. l=r+1;
  107. }
  108. return res;
  109. }
  110. long long getdmt(long long A,long long B,long long C,long long t){
  111. if(t==0){
  112. long long l=1,r,res=1;
  113. while(l<=min(A,B)){
  114. r=min(A/(A/l),B/(B/l));
  115. res=(res*ksm(f1[r]*g1[l-1]%p,(A/l)*(B/l)%(p-1),p))%p;
  116. l=r+1;
  117. }
  118. return ksm(res,C,p);
  119. }
  120. if(t==1){
  121. long long l=1,r,res=1;
  122. while(l<=min(A,B)){
  123. r=min(A/(A/l),B/(B/l));
  124. res=(res*ksm(f2[r]*g2[l-1]%p,getsum(A/l,p-1)*getsum(B/l,p-1)%(p-1),p))%p;
  125. l=r+1;
  126. }
  127. return ksm(res,getsum(C,p-1),p);
  128. }
  129. if(t==2){
  130. long long l=1,r,res=1;
  131. while(l<=min(min(A,B),C)){
  132. r=min(min(A/(A/l),B/(B/l)),C/(C/l));
  133. res=res*ksm(getres(A/l,B/l),(sum[r]-sum[l-1]+(p-1))%(p-1)*(C/l)%(p-1),p)%p*ksm(f3[r]*g3[l-1]%p,(A/l)*(B/l)%(p-1)*(C/l)%(p-1),p)%p;
  134. l=r+1;
  135. }
  136. return res;
  137. }
  138. }
  139. long long getans(long long A,long long B,long long C,long long t){
  140. long long nmt=getnmt(A,B,C,t)*getnmt(B,A,C,t)%p,dmt=getdmt(A,B,C,t)*getdmt(A,C,B,t)%p;
  141. return nmt*inv(dmt,p)%p;
  142. }
  143. int main(){
  144. scanf("%lld%lld",&T,&p);
  145. init();
  146. while(T--){
  147. scanf("%lld%lld%lld",&A,&B,&C);
  148. printf("%lld %lld %lld\n",getans(A,B,C,0),getans(A,B,C,1),getans(A,B,C,2));
  149. }
  150. return 0;
  151. }

10.例题8:[SDOI2018]旧试题
题意:求其中的因数个数
我有一个绝妙的想法,但是这里空白太小,写不下
题解:[SDOI2018]旧试题解题报告
代码:

  1. #include<stdio.h>
  2. #include<vector>
  3. #include<string.h>
  4. using namespace std;
  5. const long long maxn=1000005,maxm=1000005,mod=1000000007;
  6. long long i,j,k,a,b,c,e,mx,mn,T,cnt,tot,ans;
  7. long long lst[maxn],d[maxn],p[maxn],mu[maxn],ok[maxn],ord[maxn],deg[maxn],f[maxn],from[maxm],to[maxm],lcm[maxm],mrk[maxn];
  8. vector<long long>v[maxn],w[maxn];
  9. inline long long min(long long a,long long b){
  10. return a<b? a:b;
  11. }
  12. inline long long max(long long a,long long b){
  13. return a>b? a:b;
  14. }
  15. inline long long gcd(long long a,long long b){
  16. return b==0? a:gcd(b,a%b);
  17. }
  18. int main(){
  19. p[1]=mu[1]=1;
  20. for(i=2;i<maxn;i++){
  21. if(p[i]==0)
  22. lst[++cnt]=i,mu[i]=-1;
  23. for(j=1;j<=cnt;j++){
  24. if(i*lst[j]>=maxn)
  25. break;
  26. p[i*lst[j]]=1;
  27. if(i%lst[j]==0){
  28. mu[i*lst[j]]=0;
  29. break;
  30. }
  31. mu[i*lst[j]]=-mu[i];
  32. }
  33. }
  34. for(i=1;i<maxn;i++)
  35. if(mu[i]!=0)
  36. ok[++tot]=i,ord[i]=tot;
  37. for(i=1;i<maxn;i++){
  38. for(j=1;i*j<maxn;j++)
  39. d[i*j]++;
  40. f[i]=(f[i-1]+d[i])%mod;
  41. }
  42. scanf("%lld",&T);
  43. while(T--){
  44. memset(deg,0,sizeof(deg));
  45. memset(v,0,sizeof(v));
  46. memset(w,0,sizeof(w));
  47. scanf("%lld%lld%lld",&a,&b,&c);
  48. mn=min(min(a,b),c),mx=max(max(a,b),c);
  49. e=ans=0;
  50. for(i=1;i<=tot;i++){
  51. if(ok[i]>mx)
  52. break;
  53. for(j=1;j<=tot;j++){
  54. if(ok[i]*ok[j]>mx)
  55. break;
  56. if(mu[ok[i]*ok[j]]==0)
  57. continue;
  58. for(k=j+1;k<=tot;k++){
  59. if(ok[i]*ok[j]*ok[k]>mx)
  60. break;
  61. if(mu[ok[i]*ok[k]]==0||gcd(ok[j],ok[k])>1)
  62. continue;
  63. from[++e]=ord[ok[i]*ok[j]],to[e]=ord[ok[i]*ok[k]],lcm[e]=ok[i]*ok[j]*ok[k];
  64. deg[from[e]]++,deg[to[e]]++;
  65. }
  66. }
  67. }
  68. for(i=1;i<=tot;i++){
  69. if(ok[i]>mn)
  70. break;
  71. ans+=mu[ok[i]]*mu[ok[i]]*mu[ok[i]]*f[a/ok[i]]*f[b/ok[i]]*f[c/ok[i]];
  72. }
  73. for(i=1;i<=e;i++){
  74. v[from[i]].push_back(to[i]),w[from[i]].push_back(lcm[i]);
  75. v[to[i]].push_back(from[i]),w[to[i]].push_back(lcm[i]);
  76. }
  77. for(i=1;i<=tot;i++){
  78. if(ok[i]>min(a,b))
  79. break;
  80. for(j=0;j<v[i].size();j++){
  81. long long x=ok[i],y=ok[v[i][j]],z=w[i][j];
  82. ans=(ans+mu[x]*mu[y]*mu[y]*f[a/z]*f[b/z]*f[c/y]%mod+mod)%mod;
  83. ans=(ans+mu[x]*mu[x]*mu[y]*f[a/x]*f[b/z]*f[c/z]%mod+mod)%mod;
  84. ans=(ans+mu[x]*mu[x]*mu[y]*f[a/z]*f[b/x]*f[c/z]%mod+mod)%mod;
  85. }
  86. }
  87. memset(v,0,sizeof(v));
  88. memset(w,0,sizeof(w));
  89. for(i=1;i<=e;i++){
  90. if(deg[from[i]]>=deg[to[i]])
  91. v[from[i]].push_back(to[i]),w[from[i]].push_back(lcm[i]);
  92. else v[to[i]].push_back(from[i]),w[to[i]].push_back(lcm[i]);
  93. }
  94. for(i=1;i<=tot;i++){
  95. if(ok[i]>mx)
  96. break;
  97. for(j=0;j<v[i].size();j++)
  98. mrk[v[i][j]]=w[i][j];
  99. for(j=0;j<v[i].size();j++){
  100. long long x=v[i][j];
  101. for(k=0;k<v[x].size();k++){
  102. long long y=v[x][k],p=mrk[y],q=w[i][j],r=w[x][k];
  103. if(mrk[y]==0)
  104. continue;
  105. long long st1,st2,st3,st4,st5,st6;
  106. st1=f[a/p]*f[b/q]*f[c/r]%mod;
  107. st2=f[a/p]*f[b/r]*f[c/q]%mod;
  108. st3=f[a/q]*f[b/p]*f[c/r]%mod;
  109. st4=f[a/q]*f[b/r]*f[c/p]%mod;
  110. st5=f[a/r]*f[b/p]*f[c/q]%mod;
  111. st6=f[a/r]*f[b/q]*f[c/p]%mod;
  112. ans=(ans+mu[ok[i]]*mu[ok[x]]*mu[ok[y]]*(st1+st2+st3+st4+st5+st6)%mod+mod)%mod;
  113. }
  114. }
  115. for(j=0;j<v[i].size();j++)
  116. mrk[v[i][j]]=0;
  117. }
  118. printf("%lld\n",(ans+mod)%mod);
  119. }
  120. return 0;
  121. }

11.例题9:P4240 毒瘤之神的考验
题意:求
我有一个绝妙的想法,但是这里空白太小,写不下
题解:P4240 毒瘤之神的考验解题报告

  1. #include<stdio.h>
  2. #include<vector>
  3. #define int long long
  4. using namespace std;
  5. const int maxn=100005,mod=998244353,maxt=55;
  6. int T,n,m,cnt,t;
  7. int a[maxn],p[maxn],miu[maxn],phi[maxn],nphi[maxn],f[maxn];
  8. vector<int>g[maxn],S[maxt][maxt];
  9. int ksm(int a,int b){
  10. int res=1;
  11. while(b){
  12. if(b&1)
  13. res=res*a%mod;
  14. a=a*a%mod,b>>=1;
  15. }
  16. return res;
  17. }
  18. void init(){
  19. t=50;
  20. p[1]=miu[1]=phi[1]=1;
  21. for(int i=2;i<maxn;i++){
  22. if(p[i]==0)
  23. a[++cnt]=i,miu[i]=-1,phi[i]=i-1;
  24. for(int j=1;j<=cnt;j++){
  25. if(i*a[j]>=maxn)
  26. break;
  27. p[i*a[j]]=1;
  28. if(i%a[j]==0){
  29. miu[i*a[j]]=0;
  30. phi[i*a[j]]=phi[i]*a[j];
  31. break;
  32. }
  33. miu[i*a[j]]=-miu[i];
  34. phi[i*a[j]]=phi[i]*(a[j]-1);
  35. }
  36. }
  37. for(int i=1;i<maxn;i++)
  38. nphi[i]=ksm(phi[i],mod-2);
  39. for(int i=1;i<maxn;i++)
  40. for(int j=1;i*j<maxn;j++)
  41. f[i*j]=(f[i*j]+i*nphi[i]%mod*miu[j])%mod;
  42. for(int i=1;i<maxn;i++){
  43. g[i].push_back(0);
  44. for(int j=1;i*j<maxn;j++)
  45. g[i].push_back((g[i][j-1]+phi[i*j])%mod);
  46. }
  47. for(int i=1;i<=t;i++)
  48. for(int j=1;j<=t;j++){
  49. S[i][j].push_back(0);
  50. for(int k=1;k*max(i,j)<maxn;k++)
  51. S[i][j].push_back((S[i][j][k-1]+f[k]*g[k][i]%mod*g[k][j]%mod)%mod);
  52. }
  53. }
  54. int solve(int n,int m){
  55. int res=0,l,r;
  56. for(int i=1;i<=max(n,m)/t;i++)
  57. res=(res+f[i]*g[i][n/i]%mod*g[i][m/i]%mod)%mod;
  58. l=max(n,m)/t+1;
  59. while(l<=min(n,m)){
  60. r=min(n/(n/l),m/(m/l));
  61. res=(res+(S[n/l][m/l][r]-S[n/l][m/l][l-1]+mod)%mod)%mod;
  62. l=r+1;
  63. }
  64. return res;
  65. }
  66. signed main(){
  67. init();
  68. scanf("%lld",&T);
  69. while(T--){
  70. scanf("%lld%lld",&n,&m);
  71. printf("%lld\n",solve(n,m));
  72. }
  73. return 0;
  74. }

完结撒花!

续集:杜教筛学习笔记

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注