码迷,mamicode.com
首页 > 其他好文 > 详细

莫比乌斯函数与杜教筛

时间:2018-01-06 00:03:46      阅读:263      评论:0      收藏:0      [点我收藏+]

标签:fine   bsp   情况下   can   函数   swap   用处   break   理解   

前人的文章已经很详尽了,这里只作一点补充。

莫比乌斯反演与莫比乌斯函数入门资料: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

要说的也就这些了,刷题才是理解的最好方式。

莫比乌斯函数与杜教筛

标签:fine   bsp   情况下   can   函数   swap   用处   break   理解   

原文地址:https://www.cnblogs.com/HocRiser/p/8207284.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!