In solitute,where we |

Luisvacson

园龄:3年6个月粉丝:5关注:0

浅谈莫比乌斯反演

狄利克雷卷积

定义两个数论函数的狄利克雷卷积为:

fg=d|nf(d)g(nd)

其中单位元e(n)=[n=1]

可以证明狄利克雷卷积满足交换律及结合律

莫比乌斯函数

对于n=i=1kpici(piPrime),定义莫比乌斯函数μ(n)

μ(n)={1(n=1)0(ci2)(1)k(i[1,k],ci<2)

对于莫比乌斯函数,存在一个很重要的性质:

d|nμ(d)=e(n)=[n=1]

证明:

我们考虑枚举n的所有因子,显然某个质因子的指数2时该函数值为0,对结果不产生贡献,故当我们对于n=i=1kpici定义x=i=1kpi时,有:

d|nμ(d)=d|xμ(d)

考虑x的所有质因子p1,p2,p3pk,枚举因数等价于从中选择任意个数的质因子进行组合,所以显然有:

d|xμ(d)=i=0k(ki)(1)i

反向利用二项式定理:

(1)i=0k(ki)(1)i=i=0k(ki)(1)i×1ki=(1+1)k=0k

显然原式当且仅当k=0时等于1,否则等于0

k=0时有n=1,故我们证明了:

d|nμ(d)=[n=1]=e(n)

同时该结论也可以写成狄利克雷卷积的形式:

e=μ1

注意到莫比乌斯函数是积性函数,所以很好求,线性筛就可以了

inline void init(int n) {
    int i, j;
    mu[1] = 1;
    for (i = 1; i <= n; ++i) isprime[i] = 1;
    for (i = 2; i <= n; ++i) {
        if (isprime[i]) prime.push_back(i), mu[i] = -1;
        for (j = 0; j < prime.size() && i * prime[j] <= n; ++j) {
            isprime[i * prime[j]] = 0;
            if (!(i % prime[j])) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = mu[i] * mu[prime[j]];
        }
    }
}

莫比乌斯反演

对于数论函数f(n),g(n),如果有:

f(n)=d|ng(d)

则可以推出:

g(n)=d|nf(d)μ(nd)

证明:

将等式右边写成狄利克雷卷积的形式:

d|nf(d)μ(nd)=fμ

同样将条件写成狄利克雷卷积的形式:

f(n)=d|ng(d)=g1

所以:

(2)d|nf(d)μ(nd)=fμ=g1μ=g(1μ)=ge=g

即:

g(n)=d|nf(d)μ(nd)

于是我们证明了莫比乌斯反演定理

易证上式也有个等价形式:

g(n)=d|nμ(d)f(nd)

同时我们还有第二种反演形式:

g(n)=n|df(d)

则同样可以推出

f(n)=n|dμ(dn)g(d)

证明:

d=kn

(3)n|dμ(dn)g(d)=k1μ(k)g(nk)=k1μ(k)nk|df(d)=n|df(d)k|dnμ(k)=n|df(d)[dn=1]=f(n)

整除分块

i=1nni

显然暴力计算复杂度为O(n),但我们注意到很多值其实是一样的,所以我们可以把值相同的统一计算,分块处理:

for (int l = 1, r; l <= n; l = r + 1) {
    r = (n / (n / l));
    ans += (r - l + 1) * (n / l);
}

复杂度O(n)

例题

[POI2007]ZAP-Queries

T组询问,给出a,b,d,求i=1aj=1b[gcd(i,j)=d]
1T5×104
1da,b5×104

Solution 1

我们设:

f(k)=i=1aj=1b[gcd(i,j)=k]g(n)=n|kf(k)

可以发现f(d)就是我们要求的答案

显然根据莫比乌斯反演定理可以得到:

f(n)=n|dg(d)μ(dn)

注意到:

(4)g(n)=n|kf(k)=n|ki=1aj=1b[gcd(i,j)=k]=anbn

所以:

(5)f(n)=n|dg(d)μ(dn)=n|dμ(dn)adbd

dn=t,枚举t

f(n)=t=1min(an,bn)μ(t)antbnt

前面的μ(t)可以前缀和预处理,后面用整除分块即可

总复杂度O(Tmin(a,b))

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

int mu[MAXN], sum[MAXN];
bool isprime[MAXN];
vector<int> prime;
inline void init(int n) {
    int i, j;
    mu[1] = 1;
    for (i = 1; i <= n; ++i) isprime[i] = 1;
    for (i = 2; i <= n; ++i) {
        if (isprime[i]) prime.push_back(i), mu[i] = -1;
        for (j = 0; j < prime.size() && i * prime[j] <= n; ++j) {
            isprime[i * prime[j]] = 0;
            if (!(i % prime[j])) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = mu[i] * mu[prime[j]];
        }
    }

    for (i = 1; i <= n; ++i) sum[i] = sum[i - 1] + mu[i];
}

int T, a, b, d;
signed main() {
    init(MAXN);
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &a, &b, &d);
        int n = min(a / d, b / d);
        int l, r;

        int ans = 0;
        for (l = 1; l <= n; l = r + 1) {
            r = min((a / d) / ((a / d) / l), (b / d) / ((b / d) / l));
            ans += ((a / d) / l) * ((b / d) / l) * (sum[r] - sum[l - 1]);
        }
        printf("%d\n", ans);
    }

    return 0;
}

Solution 2

可以发现上述利用莫比乌斯反演定理构造函数进行求解的过程不太好想,其实我们还可以利用莫比乌斯函数的性质顺向思考求解

我们要求:

i=1aj=1b[gcd(i,j)=x]

(这里用x替换d避免变量名重复)

约去x

i=1axj=1bx[gcd(i,j)=1]

莫比乌斯函数有个性质:

d|nμ(d)=[n=1]

所以:

[gcd(i,j)=1]=d|gcd(i,j)μ(d)

带回原式:

i=1axj=1bxd|gcd(i,j)μ(d)

枚举d

d=1min(ax,bx)i=1axj=1bx[d|gcd(i,j)]

可以发现当且仅当i,j均为d的倍数时d|gcd(i,j),同时在1nd的倍数有nd个,所以原式等价于:

d=1min(ax,bx)μ(d)adxbdx

结果与Solution 1一致

[HAOI2011]Problem b

T组询问,给出a,b,c,d,k,求i=abj=cd[gcd(i,j)=k]
1T5×104
1ka,cb,d5×104

做法和上一题几乎完全一致,容斥一下即可

令:

solve(a,b,c,d)=i=abj=cd[gcd(i,j)=k]

显然有:

ans=solve(1,b,1,d)solve(1,b,1,c1)solve(1,a1,1,d)+solve(1,a1,1,c1)

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

int mu[MAXN], sum[MAXN];
bool isprime[MAXN];
vector<int> prime;
inline void init(int n) {
    int i, j;
    mu[1] = 1;
    for (i = 1; i <= n; ++i) isprime[i] = 1;
    for (i = 2; i <= n; ++i) {
        if (isprime[i]) prime.push_back(i), mu[i] = -1;
        for (j = 0; j < prime.size() && i * prime[j] <= n; ++j) {
            isprime[i * prime[j]] = 0;
            if (!(i % prime[j])) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = mu[i] * mu[prime[j]];
        }
    }

    for (i = 1; i <= n; ++i) sum[i] = sum[i - 1] + mu[i];
}

inline int solve(int a, int b, int d) {
    int n = min(a / d, b / d);
    int l, r;

    int ans = 0;
    for (l = 1; l <= n; l = r + 1) {
        r = min((a / d) / ((a / d) / l), (b / d) / ((b / d) / l));
        ans += ((a / d) / l) * ((b / d) / l) * (sum[r] - sum[l - 1]);
    }

    return ans;
}

int T, a, b, c, d, k;
signed main() {
    init(MAXN);
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
        printf("%d\n", solve(b, d, k) - solve(b, c - 1, k) -
                           solve(a - 1, d, k) + solve(a - 1, c - 1, k));
    }

    return 0;
}

P2257 YY的GCD

给定 N,M,求 1xN,1yMgcd(x,y) 为质数的 (x,y) 有多少对。

简化题意就是要求:

i=1nj=1m[gcd(i,j)Prime]

Solution 1

枚举质数:

pPrimei=1nj=1m[gcd(i,j)=p]

提出p

pPrimei=1npj=1mp[gcd(i,j)=1]

替换[gcd(i,j)=1]

pPrimei=1npj=1mpd|gcd(i,j)μ(d)

枚举d

pPrimed=1min(np,mp)μ(d)i=1npj=1mp[d|gcd(i,j)]

显然由于之前的经验上式等于:

pPrimed=1min(np,mp)μ(d)npdmpd

k=pd,枚举k

k=1min(n,m)pPrime,p|kμ(kp)nkmk

f(x)=pPrime,p|xμ(xp),则原式等于:

k=1min(n,m)f(k)nkmk

注意到如果我们求出了f的前缀和,即可像之前一样通过整除分块快速求解

考虑f(x)的取值:

x=i×y,其中yx的最小质因子

1.xPrime,显然f(x)=1

否则,考虑μ(xp)=μ(i×yp)=μ(i)×μ(yp)=μ(ip)×μ(y)

2.imody=0

2.1μ(i)0,则当切仅当枚举的p=yf(x)0,所以有f(x)=μ(i)

2.2μ(i)=0,则无论何时f(x)=0,所以有f(x)=μ(i)

3.imody0,则μ(xp)=μ(ip)×μ(y)=μ(ip),即f(x)中包含f(i),同时和f(i)相比f(x)多出的一项就是当p=y时,f(x)=μ(i×yy)=μ(i),所以f(x)=f(i)+μ(i)

于是我们得到了f(x)的方程:

f(x)={1xPrimeμ(i)imody=0f(i)+μ(i)imody0

可以通过线性筛求出f(x)的前缀和

#include <bits/stdc++.h>
using namespace std;
#define MAXN 10000005
#define int long long

int mu[MAXN], sum[MAXN], f[MAXN];
bool isprime[MAXN];
vector<int> prime;
inline void init(int n) {
    int i, j;
    mu[1] = 1;
    for (i = 1; i <= n; ++i) isprime[i] = 1;
    for (i = 2; i <= n; ++i) {
        if (isprime[i]) prime.push_back(i), mu[i] = -1, f[i] = 1;
        for (j = 0; j < prime.size() && i * prime[j] <= n; ++j) {
            isprime[i * prime[j]] = 0;
            if (!(i % prime[j])) {
                f[i * prime[j]] = mu[i];
                mu[i * prime[j]] = 0;
                break;
            } else {
                f[i * prime[j]] = -f[i] + mu[i];
                mu[i * prime[j]] = mu[i] * mu[prime[j]];
            }
        }
        f[i] += f[i - 1];
    }
}

int T, a, b, d;
signed main() {
    init(MAXN);
    scanf("%lld", &T);
    while (T--) {
        scanf("%lld%lld", &a, &b);
        int n = min(a, b);
        int l, r;

        int ans = 0;
        for (l = 1; l <= n; l = r + 1) {
            r = min(a / (a / l), b / (b / l));
            ans += (a / l) * (b / l) * (f[r] - f[l - 1]);
        }
        printf("%lld\n", ans);
    }

    return 0;
}

在你谷因为用了vector所以上述代码吸氧才能过

Solution 2

我们以前做过这道题:

i=1aj=1b[gcd(i,j)=1]

当时我们利用了d|nμ(d)=[n=1]这个性质

现在我们要求:

i=1aj=1b[gcd(i,j)Prime]

那我们也可以自己构造一个函数满足d|nf(d)=[nPrime]

我们先给f赋初值:

f(x)=[xPrime]

考虑如何让d|nf(d)=[nPrime]

for (i = 2; i <= MAXN; ++i) {
        for (j = i << 1; j <= MAXN; j += i) {
            f[j] -= f[i];
        }
    }

不难发现如此处理即可得到合法的f

[SDOI2015]约数个数和

d(x)x的约数个数,T组询问,求x=1ny=1md(xy)
1T,n,m5×104

首先有一个结论:

d(xy)=i|xj|y[gcd(i,j)=1]

证明:

x=i=1kpiai,y=i=1kpibi(不存在该质因子则指数为0),显然有:

d(xy)=i=1k(ai+bi+1)

对于某个质因子p,考虑等式右边如果要满足gcd(i,j)=1,显然不能同时拥有p因子。令x中含有pay中含有pb

1.如果i中不含p因子,则j中可选质因子p次数为[0,b]

2.如果j中不含p因子,则i中可选质因子p次数为[0,a]

二者相加再减去i,j都不含p的一种情况,结果数为a+b+1

考虑乘法原理,对于每个pi结果数都是ai+bi+1,所以:

i|xj|y[gcd(i,j)=1]=i=1k(ai+bi+1)

证毕

有了上述结论,我们就可以很轻松的化简原式:

x=1ny=1mi|xj|y[gcd(i,j)=1]

枚举i,j

i=1nj=1mnimj[gcd(i,j)=1]

设:

f(x)=i=1nj=1mnimj[gcd(i,j)=x]g(x)=x|df(d)

莫比乌斯反演:

f(n)=n|dμ(dn)g(d)

注意到:

g(x)=i=1nj=1mnimj[x|gcd(i,j)]

约去x

g(x)=i=1nxj=1mxnixmjx

显然答案为:

f(1)=1|dμ(d1)g(d)=i=1nμ(i)g(i)

可以整除分块预处理出s(n)=i=1nni,从而快速求出g(i)

#include <bits/stdc++.h>
using namespace std;
#define MAXN 50005
#define int long long

int mu[MAXN], sum[MAXN], s[MAXN];
bool isprime[MAXN];
vector<int> prime;
inline void init(int n) {
    int i, j;
    mu[1] = 1;
    for (i = 1; i <= n; ++i) isprime[i] = 1;
    for (i = 2; i <= n; ++i) {
        if (isprime[i]) prime.push_back(i), mu[i] = -1;
        for (j = 0; j < prime.size() && i * prime[j] <= n; ++j) {
            isprime[i * prime[j]] = 0;
            if (!(i % prime[j])) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = mu[i] * mu[prime[j]];
        }
    }

    for (i = 1; i <= n; ++i) sum[i] = sum[i - 1] + mu[i];
    for (i = 1; i <= n; ++i) {
        int ans = 0;
        int l, r;
        for (l = 1; l <= i; l = r + 1) {
            r = i / (i / l);
            ans += (i / l) * (r - l + 1);
        }
        s[i] = ans;
    }
}

int T, a, b;
signed main() {
    init(MAXN);
    scanf("%lld", &T);
    while (T--) {
        scanf("%lld%lld", &a, &b);
        int n = min(a, b);
        int l, r;

        int ans = 0;
        for (l = 1; l <= n; l = r + 1) {
            r = min(a / (a / l), b / (b / l));
            ans += s[a / l] * s[b / l] * (sum[r] - sum[l - 1]);
        }
        printf("%lld\n", ans);
    }

    return 0;
}

[国家集训队]Crash的数字表格

给出n,m,求i=1nj=1mlcm(i,j)
1n,m107
答案对20101009取模

(6)i=1nj=1mlcm(i,j)=i=1nj=1mijgcd(i,j)=x=1min(n,m)i=1nj=1m[gcd(i,j)=x]ijx=x=1min(n,m)xi=1nxj=1mx[gcd(i,j)=1]ij=x=1min(n,m)xi=1nxj=1mxd|gcd(i,j)μ(d)ij=x=1min(n,m)xd=1min(nx,mx)μ(d)i=1nxj=1mxij[d|gcd(i,j)]

i=ud,j=vd,则显然有d|gcd(i,j)

所以:

(7)原式=x=1min(n,m)xd=1min(nx,mx)μ(d)ud=1nxvd=1mxd2uv=x=1min(n,m)xd=1min(nx,mx)d2μ(d)u=1ndxuv=1mdxv

最后两项显然是等差数列求和,可以用整除分块优化

#include <bits/stdc++.h>
using namespace std;
#define MAXN 10000005
#define mod 20101009
#define int long long

int mu[MAXN], sum[MAXN], s[MAXN];
bool isprime[MAXN];
vector<int> prime;
inline void init(int n) {
    int i, j;
    mu[1] = 1;
    for (i = 1; i <= n; ++i) isprime[i] = 1;
    for (i = 2; i <= n; ++i) {
        if (isprime[i]) prime.push_back(i), mu[i] = -1;
        for (j = 0; j < prime.size() && i * prime[j] <= n; ++j) {
            isprime[i * prime[j]] = 0;
            if (!(i % prime[j])) {
                mu[i * prime[j]] = 0;
                break;
            }
            mu[i * prime[j]] = mu[i] * mu[prime[j]];
        }
    }

    for (i = 1; i <= n; ++i)
        sum[i] = (sum[i - 1] + mu[i] * 1ll * i % mod * 1ll * i % mod) % mod;
}

int a, b;
signed main() {
    scanf("%lld%lld", &a, &b);
    init(min(a, b));
    int n = min(a, b);
    int l, r, i, j;

    int inv = (mod + 1ll) / 2ll;

    int ans = 0, tsum = 0;
    for (i = 1; i <= n; ++i) {
        int x = a / i, y = b / i;
        int nn = min(x, y);
        tsum = 0;
        for (l = 1; l <= nn; l = r + 1) {
            r = min(x / (x / l), y / (y / l));
            tsum =
                (tsum +
                 (sum[r] - sum[l - 1]) % mod * 1ll *
                     (((1 + (x / l)) % mod * 1ll * (x / l) % mod) * inv % mod) %
                     mod * 1ll *
                     (((1 + (y / l)) % mod * 1ll * (y / l) % mod) * inv % mod) %
                     mod) %
                mod;
        }
        ans = (ans + tsum * i % mod) % mod;
    }
    printf("%lld\n", (ans % mod + mod) % mod);

    return 0;
}

posted @   Luisvacson  阅读(60)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起