莫比乌斯与积性函数

莫比乌斯与积性函数

之前做过不少的数论题,关于莫比乌斯与积性函数的数论题挺多的。。。特地过来总结一下。。当作自己的一个回顾了-_-

先安利一下神犇tls的博客和神犇PoPoQQQ的pdf !
膜拜tls…
跪popoqqq…
还有IOI金牌神犇任之州的集训队论文,都是好文啊!

需要先知道线性筛这个东西。。
orz…
线性筛的思想是每个合数都只会被它最小的质因数筛去,通过线性筛,我们可以O(n)得到1到n内一些数论函数的值,比如说欧拉函数、莫比乌斯函数、因子个数等等。。。

本文的除法均为整除,除非特别指出。
[expresion]为bool表达式,值为0或1,当且仅当expresion为真时为1,否则为0。
(i,j)表示gcd(i,j)。

莫比乌斯反演

莫比乌斯函数

首先定义莫比乌斯函数

u(i)=1,(1)k,0,if n = 1if n=p1p2...pk 

根据上面的定义,n大于1时且n是平方因子数时莫比乌斯函数值为0,否则从n的唯一分解定理中根据素数的个数取奇偶即可。

莫比乌斯函数的性质:

1) d|nu(d)=1,0,if n = 1
有了上述这个式子,我们就可以直接简单地以O(nlogn)筛出1到n内所有数的莫比乌斯函数值了~
至此我们已经有两种办法求1到n内所有数的欧拉函数值了。

//O(n)
bool vis[N];
int primes[N], miu[N];
int init(int n) {
    int tot = 0;
    miu[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            primes[tot++] = i;
            miu[i] = -1;
        }
        for (int j = 0; j < tot; j++) {
            int k = i * primes[j];
            if (k > n)break;
            vis[k] = true;
            if (i % primes[j])  miu[k] = -miu[i];
            else break;
        }
    }
}
//O(nlogn)
void init(int n) {
    miu[1] = 1;
    int t = n >> 1;
    for (int i = 1; i <= t; i++) if (miu[i]) {
        for (int j = i << 1; j <= n; j += i) miu[j] -= miu[i];
    }
}

2) d|nu(d)d=φ(n)n

莫比乌斯函数除了可以用在莫比乌斯反演中之外,还可以用来进行容斥。
举一个常见的例子,求取1到n这个区间内有多少个数与x互质,一般的做法是直接进行容斥,但是我们可以发现容斥的系数刚好是莫比乌斯函数,即ans = d|xu(d)nd ,其实这两者从本质考虑是完全等价的。

莫比乌斯反演

莫比乌斯反演是一个这样的式子:
定义 F(n)=d|nf(n) ,那么可以得到 f(n)=d|nu(nd)F(d)
莫比乌斯反演还有一种更常见的形式: F(n)=n|df(d) , 那么有 f(n)=n|du(dn)F(d)
一般应用的都是上述的第二种形式,证明可以通过归纳得出,也可以直接通过式子变换得出,还可以由狄利克雷卷积证明。
f(n)=d|nu(nd)F(d)=d|nu(nd)x|df(x)=d|nf(d)x|ndu(x)=f(n)
上述的证明中给出了一种常见的和式变换技巧:交换求和顺序。通过交换求和顺序,我们往往可以将某些和式化简或者更容易求出该和式,后面公式的化简将多次用到。

莫比乌斯反演还有一个推广式,如下:
这里写图片描述

积性函数

积性函数定义

f(x) 为一个数论函数,如果对于任意正整数a、b满足 (a,b)=1 ,有 f(ab)=f(a)f(b) 的话,我们称 f(n) 为积性函数;如果对于任意正整数啊a、b有 f(ab)=f(a)f(b) 的话,我们称 f(n) 为完全积性函数。
常见的积性函数有:
因子个数函数 d(n) ,因子和函数 σ(n) ,二元函数 gcd(a,b) ,欧拉函数 φ(n) ,莫比乌斯函数 u(n)
完全积性函数有:
元函数 e(n)=[n==1] ,恒等函数 I(n)=1 ,单位函数 id(n)=n

积性函数应用

我们来看看积性函数的应用:
如果 f(x) 为积性函数且 n=ti=1peii ,那么可以得到 f(n)=ti=1f(peii) ,如果f(x)为完全积性函数,那么我们还可以进一步得到 f(n)=ti=1f(pi)ei
举个简单的例子:因子个数 d(n)=ti=1d(peii)=ti=1(ei+1) ,因子和函数 σ(n)=ti=1σ(peii)=ti=1piei+11pi1
积性函数还可以用来进行线性筛从而得到很多数论函数的值~
比如下面的欧拉函数:
可以知道当 i%p==0 时, φ(ip)=φ(i)p ,而当 i%p!=0 时,有

φ(ip)=φ(i)φ(p)=φ(i)(p1)

const int N = 1e6 + 5;
bool vis[N];
int phi[N], p[N], cnt = 0;
void seive() {
    cnt = 0;
    phi[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!vis[i]) p[cnt++] = i, phi[i] = i - 1;
        for (int j = 0; j < cnt; j++) {
            int s = i * p[j];
            if (s > N) break;
            vis[s] = 1;
            if (i % p[j] == 0) {
                phi[s] = p[j] * phi[i];
                break;
            }
            phi[s] = (p[j] - 1) * phi[i];
        }
    }
}

积性函数前缀和

积性函数的和也是积性函数,积性函数前缀和是一个常见的问题,常常需要低于线性时间内解决。

狄利克雷卷积

狄利克雷卷积是解决积性函数前缀和问题的一个重要工具。

定义

对两个算术函数f, g,定义其Dirichlet卷积为新函数f * g,满足 (fg)(n)=d|nf(d)g(nd)
狄利克雷卷积满足以下定律:
这里写图片描述
若f,g均为积性函数,则f*g也是积性函数。
我们可以O(nlogn)预处理两个函数的Dirichlet卷积。

LL f[N], g[N], h[N];
void calc(int n) {
    for (int i = 1; i * i <= n; i++) {
        h[i * i] += f[i] * g[i];
        for (int j = i + 1; i * j <= n; j++) h[i * j] += f[i] * g[j] + f[j] * g[i];
    }
}
举例

很多数论函数都可以用狄利克雷卷积来表示:
①约数个数函数 d(n)=(II)(n) ;
②约数和函数 d(n)=(Iid)(n) ;
d|nu(d)=[n==1] ,得到 Iu=e
d|nφ(d)=n ,得到 Iφ=id

现在我们可以通过狄利克雷卷积来证明莫比乌斯反演了:
如果 F(n)=d|nf(n) ,那么F = I*f,那么可以知道F*u = I*f*u=f*(I*u)=f*e=f,所以 f(n)=d|nu(nd)F(d)

应用

设f(n)为一个数论函数,需要计算 S(n)=ni=1f(i) ,通常n>=1e9。
要解决上述问题,我们可以构造一个 S(n) 关于 S(ni) 的递推式。
找到一个合适的数论函数g(n),

i=1nd|if(d)g(id)=i=1ng(i)j=1nif(j)=i=1ng(i)S(ni)

这样,可以知道 g(1)S(n)=ni=1(fg)(i)ni=2g(i)S(ni)
构造的卷积函数必须容易求得前缀和,这样我们可以记忆化地计算S(n),因为 ni 是分段的,可以计算出复杂度为 O(n34) ,只要我们预处理S函数前 23 的值,复杂度就可以达到 O(n23)
上述的办法就是传说中的杜教筛了~
如果找不到适合的函数g(n),那么可以采用一种复杂度为 O(n34logn) 的办法来实现求和,详见任之州的论文。

下面给出求积性函数前缀和时常用到的一些公式和结论:
d|nu(d)=[n==1]
d|nφ(d)=n
ni=1i[(i,n)==1]=nφ(n)+[n==1]2
d(n2)=d|n2w(d)=d|ni|du2(i) ,w(d)表示d的不同质因子的个数。
d(nm)=i|nj|m[(i,j)==1]

i=1n[(i,n)==1](i1,n)=d|ni=1n[(i,n)==1][d|(i1)]=d|nφ(d)φ(n)φ(d)=d(n)φ(n)

d|ijd(i,d)|j
上面的几个东西有些是很显然的,但是还是写出来放在一起好了,至于第六个本人能力过弱不知如何证明。。。

习题

bzoj 2420 完全平方数
传送门
题意:求第n个无平方因子数。
分析:问题等价于求最小的x使得1到x内有n个无平方因子数,我们考虑容斥,总数减掉2^2的倍数个数,3^2的倍数个数,5^2的倍数个数….加上(2*3)^2的倍数个数,加上(3*5)^2的倍数个数,加上(3*5)^2的倍数个数…..可以发现容斥的系数就是莫比乌斯函数。这样我们二分x,然后 x 求个数即可。
不过注意到m/(i*i)的值是分块的,所以我们可以分块求,所以复杂度下界大概是 x

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 41005;
bool vis[N];
int primes[N], miu[N];

int init(int limit) {
    int tot = 0;
    miu[1] = 0;
    for (int i = 2; i <= limit; i++) {
        if (!vis[i]) {
            primes[tot++] = i;
            miu[i] = 1;
        }
        for (int j = 0; j < tot; j++) {
            int k = i * primes[j];
            if (k > limit) break;
            vis[k] = true;
            if (i % primes[j]) miu[k] = -miu[i];
            else break;
        }
        miu[i] += miu[i-1];
    }
}
LL cal(LL m) {
    LL s = 0;
    for (LL i = 1, last; i * i <= m; i = last + 1) {
        LL x = m / (i * i); last= sqrt(m / x);
        s += (miu[last] - miu[i-1]) * x;
    }
    return m - s;
}
int main() {
    init(41000);
    int t;
    scanf("%d", &t);
    while (t--) {
        LL k;
        scanf("%lld", &k);
        LL l = 0, r = 1644934090LL;
        while (r - l > 1) {
            LL m = (r + l) >> 1;
            cal(m) >= k ? r = m : l = m;
        }
        printf("%lld\n", r);
    }
    return 0;
}

bzoj 2301 Problem b
传送门
题意:对于给出的 n 个询问,每次求有多少个数对 (x,y) ,满足 a ≤ x ≤ b , c ≤ y ≤ d ,且 gcd(x,y) = k , gcd(x,y) 函数为 x 和 y 的最大公约数。
分析:我们考虑容斥原理分成四段求,那么现在问题变为了求1~n/k,1到m/k内有多少对数(x,y)最大公约数为1了。

f(n,m)=i=1nj=1m[(i,j)==1]=i=1n
  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值