[关闭]
@ZYK1997 2015-04-02T03:53:20.000000Z 字数 8143 阅读 889

数论练习题

数论


Prob 1:

给你 N,M,求 Na=1Mb=1gcd(a,b)

思路一:dnφ(d)=n

推导如下:

a=1Nb=1Mgcd(a,b)

=a=1Nb=1Mdgcd(a,b)φ(d)

=dφ(d)a=1Nb=1M[dgcd(a,b)]

=dφ(d)a=1Nb=1M[da  db]

=dφ(d)a=1N[da]b=1M[db]

=dφ(d)NdMd

化简到这里,我们不禁想起了另一道经典题目:
给定n,求 Ni=1Ni
解决方法:通过观察(打表)、猜想……我们可以发现,对于很多i而言,Ni的值是相等的。不妨设 i[l,r],Ni=d,那么ri=lNi=dri=l,然而,我们可以证明[l,r]这样的区间总共不超过N个,问题可以在O(N)内解决。

我们再回到这道题来。
我们会发现,NdMd,分别有N,M个区间,那么NdMd取值不超过N+M个区间,我们可以预处理出来φ(d)的前缀和,然后枚举区间,问题可以在O(N+M)解决。
粘上代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=100000,M=N+5;
  4. #define LL long long
  5. int n,m;
  6. bool cjx[M];
  7. int prime[M],tot;
  8. int phi[M];
  9. LL sum[M];
  10. void pre(){
  11. tot=0;
  12. phi[1]=1;
  13. for(int i=2;i<=N;i++){
  14. if(!cjx[i]) {
  15. prime[tot++]=i;
  16. phi[i]=i-1;
  17. }
  18. for(int j=0;j<tot;j++){
  19. if(i*prime[j]>N) break;
  20. cjx[i*prime[j]]=true;
  21. if(i%prime[j]==0){
  22. phi[i*prime[j]]=phi[i]*prime[j];
  23. break;
  24. } else {
  25. phi[i*prime[j]]=phi[i]*(prime[j]-1);
  26. }
  27. }
  28. }
  29. for(int i=1;i<=N;i++) sum[i]=sum[i-1]+phi[i];
  30. }
  31. LL solve(int n,int m){
  32. LL re=0;
  33. for(int i=1,j;i<=min(n,m);i=j+1){
  34. j=min(n/(n/i),m/(m/i));
  35. re+=(sum[j]-sum[i-1])*(n/i)*(m/i);
  36. }
  37. return re;
  38. }
  39. int main(){
  40. pre();
  41. int T; scanf("%d",&T);
  42. while(T--){
  43. scanf("%d%d",&n,&m);
  44. cout<<solve(n,m)<<endl;
  45. }
  46. return 0;
  47. }

思路二:dnμ(d)=[n=1]

我们换一个思路,考虑d可能成为哪些i,j的最大公约数

i=1Nj=1Mgcd(i,j)

=i=1Nj=1Md[d=gcd(i,j)]d

=ddi=1Nj=1M[d=gcd(i,j)]

=ddi=1Ndj=1Md[gcd(i,j)=1]

=ddi=1Ndj=1Mddgcd(i,j)μ(d)

=dddμ(d)i=1Nd[di]j=1Md[dj]

=dddμ(d)NddMdd

=dddμ(d)NddMdd

我们设D=dd,那么d=Dd
ans=DNDMDdDμ(d)Dd

我们发现后面的H(x)=dxμ(d)xdf(x)=μ(x)g(x)=x的狄利克雷卷积,所以H(x)是积性函数,我们可以使用筛法O(N)预处理出来,剩下过程和思路一相同了。
到了这里,不知道大家有没有发现,和思路一对比之下,我们发现,H(x)=φ(x),没错!
这是巧合吗?不!
n=dnφ(d)
我们设f(x)=x,g(x)=φ(x)
f(x)=dxg(d)
我们进行莫比乌斯反演得到:
g(x)=dxμ(d)f(xd)
也就是
φ(x)=dxμ(d)xd
我们结合思路一与思路二无意中发现了莫比乌斯反演,哈哈哈。。。


Prob 2:

给你 N,M,求Na=1Mb=1lcm(a,b)

主要公式:dnμ(d)=[n=1]

推导如下:

a=1Nb=1Mlcm(a,b)

=a=1Nb=1Mabgcd(a,b)

=a=1N  b=1,d=gcd(a,b)Mabd

=da=1Nb=1Mabd[d=gcd(a,b)]

=dda=1Ndb=1Mdab[gcd(a,b)=1]

不妨设f(n,m)=na=1mb=1ab[gcd(a,b)=1]
下面化简f(n,m)=
a=1nb=1mab[gcd(a,b)=1]

=a=1nb=1mabdgcd(a,b)μ(d)

=dμ(d)dandbmab

=dμ(d)a=1ndb=1mdabd2

=dμ(d)d2a=1ndb=1mdab

=dμ(d)d2nd(nd+1)2md(md+1)2

下面我们把f(Nd,Md)代入原式:ans=
14dddμ(d)d2Ndd(Ndd+1)Mdd(Mdd+1)

=14dddμ(d)d2Ndd(Ndd+1)Mdd(Mdd+1)

D=dd
=14DND(ND+1)MD(MD+1)DdDμ(d)d

由于积性函数的莫比乌斯变换函数还是积性函数,所以DdDμ(d)d是积性函数,可以用筛法O(N),预处理出来。我们再观察前面14DND(ND+1)MD(MD+1)这一堆东西,很明显还是分块来搞。
这样一来,问题最终O(N+M)完美解决。
粘上代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=100000;
  4. const int M=100005;
  5. typedef long long LL;
  6. bool cjx[M];
  7. int prime[M],tot;
  8. int mu[M];
  9. LL g[M],sum[M];
  10. void pre(){
  11. tot=0;
  12. mu[1]=1; g[1]=1;
  13. for(int i=2;i<=N;i++){
  14. if(!cjx[i]){
  15. prime[tot++]=i;
  16. mu[i]=-1;
  17. g[i]=-i*(i-1);
  18. }
  19. for(int j=0;j<tot;j++){
  20. if(i*prime[j]>N) break;
  21. cjx[i*prime[j]]=true;
  22. if(i%prime[j]==0){
  23. mu[i*prime[j]]=0;
  24. g[i*prime[j]]=g[i]*prime[j];
  25. break;
  26. } else {
  27. mu[i*prime[j]]=-mu[i];
  28. g[i*prime[j]]=g[i]*g[prime[j]];
  29. }
  30. }
  31. }
  32. for(int i=1;i<=N;i++) sum[i]=sum[i-1]+g[i];
  33. }
  34. LL solve(int n,int m){
  35. LL re=0;
  36. for(int i=1,j;i<=min(n,m);i=j+1){
  37. j=min(n/(n/i),m/(m/i));
  38. re+=(LL)(sum[j]-sum[i-1])*(n/i)*(n/i+1)*(m/i)*(m/i+1);
  39. }
  40. return re>>2;
  41. }
  42. int main(){
  43. pre();
  44. int T; scanf("%d",&T);
  45. while(T--){
  46. int n,m;
  47. scanf("%d%d",&n,&m);
  48. cout<<solve(n,m)<<endl;
  49. }
  50. return 0;
  51. }

Prob 3

给你 n,求 ni=1gcd(i,n)

解析:
首先转化思路,我们思考d可以成为哪些in的最大公约数
ni=1gcd(i,n)=dndφ(nd)
f(x)=x,g(x)=φ(x),H(x)=dndφ(nd)
H(x)f(x)g(x)的狄利克雷卷积,所以H(x)是积性函数。
我们将n分解质因数
H(n)=H(pα11pα22pαnn)=H(pα11)H(pα22)H(pαnn)
所以我们只需要思考怎么计算H(pk)即可。
H(pk)=dpkdφ(pkd)
=ki=0piφ(pki)
而我们知道φ(pk)=pkpk1
那么我们通过数列化简、整理最终得到
H(pk)=(k+1)pkkpk1
问题解决了,我的代码比较丑,大家意识流即可……

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<cmath>
  5. #include<algorithm>
  6. using namespace std;
  7. typedef long long LL;
  8. LL pow(LL a,LL b){
  9. LL re=1;
  10. for(;b;b>>=1,a*=a)
  11. if(b&1) re*=a;
  12. return re;
  13. }
  14. int main(){
  15. LL n; cin>>n;
  16. LL ans=1;
  17. for(int i=2;i*i<=n;i++){
  18. LL a=0;
  19. while(n%i==0){
  20. a++; n/=i;
  21. }
  22. if(a==0) continue;
  23. LL b=pow(i,a-1);
  24. ans*=(a+1)*b*i-a*b;
  25. }
  26. ans*=2*n-1;
  27. cout<<ans<<endl;
  28. return 0;
  29. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注