莫比乌斯反演&杜教筛

莫比乌斯反演

几个常用符号:

$$
\varepsilon=\left\{ \begin{matrix} \ 1,\ \ while\ n=1\\0, \ \ otherwise\ \ \end{matrix} \right.\\Id_k(n)=n^k,\ \ k=1时为恒等函数Id(n)=n,\ k=0时为常数函数1(n)\\\sigma_k(n)=\sum_{d|n}d^k,\ \ k=1时为因数和函数\sigma(n),\ k=0时为因数个数函数d(n)\\ \varphi(n)=\sum_{i=1}^n[gcd(i,n)==1]\\\mu(n)=\left\{ \begin{matrix} 1,\ \ while\ n=1\qquad \qquad \qquad \qquad \qquad \qquad \\(-1)^k, \ while\ n=p_1p_2p_3...p_k, 其中p_i是不同的质数\\0,\ otherwise\qquad \qquad \qquad \qquad \qquad\ \ \ \ \qquad \end{matrix} \right.
$$

常用公式:

$$
Id_k*1=\sigma_k,\ \ k=1时, 有Id*1=\sigma, 即(Id*1)(n)=\sum_{d|n}d\\\sum_{d|n}\phi(d)=n(应用于杜教筛)\ \ 即\varphi*1=Id\\\mu*1=\varepsilon\\若F(n)=\sum_{d|n}f(d), 则f(n)=\sum_{d|n}F(d)\mu(\frac{n}{d})\ \ 即\ F=f*1\ \ <=>\ \ f=F*\mu\\ 设f_k(n) = n^k\varphi(n), 则\ f_k*Id_k=Id_{k+1} \\
$$

1. 数论分块

计算:

$$
\sum_{i = 1}^{min(n, m)}f(i)*\lfloor \frac{n}{i} \rfloor*\lfloor \frac{m}{i} \rfloor
$$

预处理f(n)的前缀和S(n), 通常f(n)为积性函数, 可以通过欧拉筛O(n)处理出来

ll get_sum(int n, int m) {
    ll res = 0;
    for (int l = 1, r; l <= min(n, m); l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        res = res + (S[r] - S[l - 1]) * (n / l) * (m / l);
    }
    return res;
}

2. 例题

T1. Luogu P3455 ZAP-Queries

$$
(使n>=m)即求: \\ \sum_{i=1}^{n}\sum_{j = 1}^m[gcd(i, j)==1] \\ = \sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}\sum_{d|gcd(i, j)}\mu(d) \\ = \sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{i=1}^{\lfloor \frac{m}{kd} \rfloor}1 \\ = \sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)*\lfloor \frac{n}{kd} \rfloor*\lfloor \frac{m}{kd} \rfloor
$$

$$
处理到这儿, 预处理\mu(n)的前缀和S(n)=\sum_{i=1}^{n}\mu(i) 通过分块, 每次查询复杂度为O(\sqrt n)
$$

ll mu[N];
int primes[N], cnt;
bool st[N];
void init(int n = N - 10) {
    /* 线性筛出莫比乌斯函数 */    
    for (int i = 1; i <= n; i++) mu[i] += mu[i - 1]; // 处理前缀和
}
​
ll query(int n, int m, int k) {
    ll res = 0;
    for (int l = 1, r; l <= m; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        res += (mu[r] - mu[l - 1]) * (n / l) * (m / l);
    }
    return res;
}
​
int main() {
    init();
    int T;
    cin >> T;
    while (T--) {
        int n, m, k;
        cin >> n >> m >> k;
        n /= k, m /= k; if (n < m) swap(n, m);
​
        cout << query(n, m, k) << '\n';
    }
    return 0;
}
​
T2. Luogu P2257 YY的GCD

$$
求\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i, j) \in Prime] \\ = \sum_{k=1, k \in Prime}^{n}\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor }\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor }[gcd(i, j) == 1] \\ = \sum_{k=1, k \in Prime}^{n}\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor }\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor } \sum_{d|gcd(i, j)}\mu(d) \\ = \sum_{k=1, k \in Prime}^{n}\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)*\lfloor \frac{n}{kd}\rfloor*\lfloor \frac{m}{kd}\rfloor \ \ (跳了一步) \\ 设\ T=kd,\ 有: \\ =\sum_{k=1, k \in Prime}^{n}\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)*\lfloor \frac{n}{T} \rfloor * \lfloor \frac{m}{T} \rfloor \\ 先枚举T: \\ =\sum_{T=1}^{n}\lfloor \frac{n}{T} \rfloor*\lfloor \frac{m}{T} \rfloor \sum_{k|T, k \in Prime}\mu(\frac{T}{k})<-----(右半部分可预处理, 每次查询O(\sqrt n))
$$

typedef long long ll;
int primes[N], mu[N], cnt;
bool st[N];
ll f[N];
​
void init(int n = N - 10) {
    mu[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[cnt++] = i;
            mu[i] = -1;
        }
        
        for (int j = 0; primes[j] <= n / i; j++) {
            st[i * primes[j]] = true;
            if (i % primes[j] == 0) break;
            mu[i * primes[j]] = -mu[i];
        }
    }
    
    for (int i = 0; i < cnt; i++) { //很实用的技巧
        int p = primes[i];
        for (int j = 1; j <= n / p; j++) {
            f[j * p] += mu[j];
        }
    }
    
    for (int i = 1; i <= n; i++) f[i] += f[i - 1];
}
​
ll query(int n, int m) {
    ll res = 0;
    for (int l = 1, r; l <= m; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        res += (f[r] - f[l - 1]) * (n / l) * (m / l);
    }
    
    return res;
}
int main() {
    init();
    int T;
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        if (n < m) swap(n, m);
        cout << query(n, m) << '\n';
    }
    return 0;
}
T3. Luogu P2522 Problem b

$$
求:\sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i, j)==k] \\ 设:f(n, m, k) = \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i, j) == k] \\ 原问题可等价于:ans=f(b,d,k)-f(a-1,d,k)+f(b,c-1,k)+f(a-1,c-1,k) \\ f(n,m,k)=\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}\sum_{d|gcd(i, j)}\mu(d)=\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{kd} \rfloor}1 =\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)\lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd}\rfloor(至此可以O(\sqrt n)完成每次询问)
$$

code:
const int N = 5e4 + 10;
bool st[N];
int primes[N], cnt, mu[N];
void init(int n = N - 10); // 线性筛处理莫比乌斯函数的前缀和
​
ll get_sum(int n, int m, int k) {
    if (n < m) swap(n, m);
    ll res = 0;
    n /= k, m /= k;
    for (int l = 1, r; l <= m; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        res += 1ll * (mu[r] - mu[l - 1]) * (n / l) * (m / l);
    }
    return res;
}
​
int main() {
    init();
    int T;
    cin >> T;
    while (T--) {
        int a, b, c, d, k;
        cin >> a >> b >> c >> d >> k;
        cout << 
            get_sum(b, d, k) - get_sum(a - 1, d, k) - get_sum(b, c - 1, k) + get_sum(a - 1, c - 1, k) 
            << '\n';
    } 
    return 0;
}
​

T4. Luogu P3327 约数个数和

前置知识:d(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j} [\gcd(x,y)=1]

$$
求\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)\\=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x, y) == 1]\\=\sum_{x=1}^{n}\sum_{y=1}^{m}[gcd(x,y)=1]\lfloor \frac{n}{x}\rfloor \lfloor \frac{m}{x}\rfloor \\=\sum_{x=1}^{n}\sum_{y=1}^{m}\sum_{d|gcd(x,y)}\mu(d)\lfloor \frac{n}{x}\rfloor \lfloor \frac{m}{x}\rfloor\\=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{y=1}^{\lfloor \frac{m}{d} \rfloor} \lfloor \frac{n}{dx} \rfloor*\lfloor \frac{m}{dy}\rfloor \\ 至此, 设g(x) = \sum_{i=1}^{\lfloor \frac{n}{x} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{x} \rfloor} \lfloor \frac{n}{ix} \rfloor*\lfloor \frac{m}{jy}\rfloor \\原式=\sum_{i=1}^{min(n,m)}\mu(i)*g(i) \\对于g(i), 先预处理出S(n) = \sum_{i=1}^{n}\lfloor \frac{n}{i}\rfloor, 就可以O(1)计算g(i) \\ 总时间复杂度O(n+T\sqrt n)
$$

code:
typedef long long ll;
const int N = 5e4 + 10;
int mu[N], primes[N], cnt, Sx_i[N];
bool st[N];
​
void init(int n = N - 10) {
    /* 先预处理出1~n中所有的莫比乌斯函数的值 */
    for (int i = 1; i <= n; i++) {
        mu[i] += mu[i - 1];
        for (int l = 1, r; l <= i; l = r + 1) {
            r = i / (i / l);
            Sx_i[i] += 1ll * (r - l + 1) * (i / l);
        }
    }
}
​
ll get_sum(int n, int m) {
    ll res = 0;
    if (n < m) swap(n, m);
    for (int l = 1, r; l <= m; l = r + 1) {
        r = min(n / (n / l), m / (m / l));
        res += 1ll * (mu[r] - mu[l - 1]) * Sx_i[n / l] * Sx_i[m / l];
    }
    return res;
}
​
int main() {
    int T;
    init();
    cin >> T;
    while (T--) {
        int n, m;
        cin >> n >> m;
        cout << get_sum(n, m) << '\n';
    }
    return 0;
}
​
T5. Luogu P3768 简单的数学题

$$
求:\sum_{i=1}^{n}\sum_{j=1}^{n}ij*gcd(i,j)=\sum_{k=1}^{n}d\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{k} \rfloor}ij*[gcd(i,j)==1]=\sum_{k=1}^{n}k^3\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{k} \rfloor}ij*\sum_{d|gcd(i, j)}\mu(d) = \sum_{k=1}^{n}k^3\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{kd} \rfloor}ijd^2\\令:Sum(n)=\sum_{i=1}^{n}i=\frac{n(n+1)}{2}\\原式=\sum_{k=1}^{n}k^3\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)*d^2*Sum^2(\lfloor\frac{n}{kd} \rfloor) \\ 令T=kd, 转而枚举T, 则:\\原式=\sum_{T=1}^{n}g^2(\lfloor \frac{n}{T}\rfloor)*T^2\sum_{d|T}\mu(d)*(\frac{T}{d})\\右式中:\sum_{d|T}\mu(d)*(\frac{T}{d})=\varphi(T)\\原式=\sum_{T=1}^{n}g^2(\lfloor \frac{n}{T} \rfloor)*T^2*\varphi(T)\\至此, 需要快速求出\sum_{i=1}^{n}i^2\varphi(i) \ \ \ \ ----->杜教筛 \\令 f(n)=n^2\varphi(n)\ \ ,(看到f这样的式子考虑)g(n)=n^2, S(n)=\sum_{i=1}^{n}f(i) \\杜教筛套路式子: S(n)=\sum_{i=1}^ni^3-\sum_{i=2}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor) \\因此原式=\sum_{T=1}^ng^2(\lfloor\frac{n}{T}\rfloor)*f(T), 对g分块, f通过杜教筛可以在O(n^{\frac{3}{4}})内求得结果
$$

code:
const int N = 8e6 + 10;
unordered_map<ll, ll> S;
ll phi[N], primes[N], cnt, p, inv4, inv6;
bool st[N];
​
ll qpow(ll x, ll n, ll p);
void init(int n) {
    inv4 = qpow(4, p - 2, p), inv6 = qpow(6, p - 2, p);
    /* 预处理phi(1~n) */
    for (int i = 1; i <= n; i++) phi[i] = (phi[i - 1] + phi[i] * i % p * i % p) % p;
}
​
ll S3(ll n) { n %= p; return n * n % p * (n + 1) % p * (n + 1) % p * inv4 % p; }
ll S2(ll n) { n %= p; return n * (n + 1) % p * (n * 2 % p + 1) % p * inv6 % p; }
​
ll getS(ll n) { // 杜教筛
    if (n <= N - 10) return S0[n];
    if (S.count(n)) return S[n];
    ll res = S3(n);
    for (ll l = 2, r; l <= n; l = r + 1) {
        r = n / (n / l);
        res = (res - (S2(r) - S2(l - 1)) % p * getS(n / l) % p + p) % p;
    }
    
    return S[n] = res;
}
​
int main() {
    ll n;
    cin >> p >> n;
    init(N - 10);
    
    ll res = 0;
    for (ll l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        res = (res + (getS(r) - getS(l - 1) + p) % p * S3(n / l) % p) % p;
    }
    
    cout << res << endl;
    return 0;
}
​

T...

杜教筛

$$
求:S(n)=\sum_{i=1}^{n}f(i), 其中f(n)是一个数论函数 \\如果能构造出g, h两个积性函数, 使得h=f*g,且满足一些条件(下面体现) \\则\sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}g(d)f(\frac{i}{d}) \\=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}f(i) \\=\sum_{i=1}^ng(i)S(\lfloor\frac{n}{i}\rfloor) \\=g(1)S(n)+\sum_{i=2}^ng(i)S(\lfloor\frac{n}{i}\rfloor)\ \ \ \ 条件就是g和h的前缀和很好求 \\g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{i=2}^ng(i)S(\lfloor \frac{n}{i} \rfloor) \\最后通过递归分块处理, 起始时可以预处理出n比较小的S(n)来减少递归层数, 进一步优化时间
$$

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值