前人的文章已经很详尽了,这里只作一点补充。
莫比乌斯反演与莫比乌斯函数入门资料:https://wenku.baidu.com/view/fbec9c63ba1aa8114431d9ac.html
讲的非常清楚,这里稍微补充一下:
1.虽然考试肯定不会考,但是对于定理的证明还是应该大概了解一下的。关于欧拉函数φ与莫比乌斯函数μ,由于它们都是积性函数,所以很多性质都可以用类似数学归纳法的方法证明。过程是:(1)对于一个性质证明在x为素数是成立 (2)对于素数p和一个正整数a,设此性质对a与p均成立,p*a成立。(3)由类似数学归纳法可知,对于全体正整数都满足性质。
2.事实上,莫比乌斯反演定理用处并不大,一般过程都会用它的“μ*I=e"性质代替。而φ*I=id的性质也经常使用。通常情况下这两个性质已经够应付大部分题目了。不过要明确所有变形的最终目的是使最终的式子可以“分块加速”:
LL solve(int n,int m){ LL res=0; if (n>m) swap(n,m); for (int i=1,l=0; i<=n; i=l+1){ l=min(n/(n/i),m/(m/i)); res+=1ll*(sum[l]-sum[i-1])*(n/i)*(m/i); } return res; }
3.对于线性筛法(也就是欧拉筛,筛素数的同时可以预处理积性函数,还可以用来预处理其它一些东西)代码大概是这样:
void init(int n){ memset(phi,-1,sizeof(phi)); memset(miu,-1,sizeof(miu)); int tot=0; miu[0]=phi[0]=0; miu[1]=phi[1]=1; rep(i,2,n){ if (phi[i]==-1) p[++tot]=i,phi[i]=i-1,miu[i]=-1; for (int j=1; j<=tot && p[j]*i<=n; j++){ if (i%p[j]) phi[i*p[j]]=phi[i]*(p[j]-1),miu[i*p[j]]=-miu[i]; else { phi[i*p[j]]=phi[i]*p[j]; miu[i*p[j]]=0; break; } } phi[i]+=phi[i-1]; miu[i]+=miu[i-1]; } }
然后就是习题:
https://www.cnblogs.com/Milkor/p/4474835.html
在草稿纸上把式子列出来就可以了,主要用来把变式练熟,翻来覆去本质上还是那两个性质和“枚举倍数”方法的应用。
不过杜教筛就没那么简单了,可以产生更多难度很高的题目。
首先要学会欧拉函数和莫比乌斯函数的前缀和求法。
http://blog.csdn.net/skywalkert/article/details/50500009
杜教筛目前最全的讲义之一,主要前面前缀和讲的很清楚。
注意存储方式,很巧妙的用线性大小的空间存储了结果(p放在n/p的位置上):
BZOJ3944(裸题)
#include<cstdio> #include<cstring> #include<algorithm> #define rep(i,l,r) for (int i=l; i<=r; i++) typedef long long ll; using namespace std; const int N=2000100,M=100100; int cas,n,m,cnt,c[N]; ll phi[N],mu[N],p[M],q[M]; bool vis[M]; ll get_p(int x){ return (x<=m) ? phi[x] : p[n/x]; } ll get_q(int x){ return (x<=m) ? mu[x] : q[n/x]; } void solve(int x){ if (x<=m) return; int i,j=1,t=n/x; if (vis[t]) return; vis[t]=1; p[t]=((ll)x+1)*x>>1; q[t]=1; while (j<x){ i=j+1; j=x/(x/i); solve(x/i); p[t]-=get_p(x/i)*(j-i+1); q[t]-=get_q(x/i)*(j-i+1); } } int main(){ scanf("%d",&cas); m=2000000; int i,j; phi[1]=mu[1]=1; for (i=2; i<=m; i++){ if (!phi[i]) phi[i]=i-1,mu[i]=-1,c[++cnt]=i; for (j=1; j<=cnt && i*c[j]<=m; j++) if (i%c[j]) phi[i*c[j]]=phi[i]*(c[j]-1),mu[i*c[j]]=-mu[i]; else{ phi[i*c[j]]=phi[i]*c[j]; mu[i*c[j]]=0; break; } } for (i=2; i<=m; i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1]; while (cas--){ scanf("%d",&n); memset(vis,0,sizeof(vis)); if (n<=m) printf("%lld %lld\\n",phi[n],mu[n]); else solve(1ll*n),printf("%lld %lld\\n",p[1],q[1]); } return 0; }
练习可以用下面两题(相对于其它题目比较简单)
BZOJ4916:http://www.cnblogs.com/Troywar/p/8029537.html
NOI2016 D1 T3:https://www.cnblogs.com/lcf-2000/p/6250330.html
要说的也就这些了,刷题才是理解的最好方式。