[SDOI2015] 约数个数和

Posted Qrsikno

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2015] 约数个数和相关的知识,希望对你有一定的参考价值。


Portal

考虑这样一个式子:
\[ d(ij) = \sum_{x | i}\sum_{y | j} [x \bot y] \]
怎么证明? 一开始我们一定会想到\[d(ij) = \sum_{x | i} \sum_{y | i} 1 \] 但这样会计算重复. 于是我们考虑:
\[ d(ij) = \sum_{x | i}\sum_{\frac{m}{y} | j}1 \]
这样每个因数就变成\[ \frac{xm}{y} \], 如果x和y不互质. 那么就会有\[ \frac{(xp)m}{yp} == \frac{xm}{p} \]

如果xp,yp同时是i, j的因数那就会算重复.

所以一定要求此二者互质.

相应的:
\[ \sigma(ij) = \sum_{x | i}\sum_{y | j} [x \bot y]x \frac{}{} \frac{i}{y} \]


那么我们可以开始化式子了!!
\[ Ans = \sum_{i}\sum_{j} \sum_{x | i}\sum_{y | j}[(x, y) == 1] \= \sum_{x} \sum_{y}[(x, y) == 1]\sum_{i} \sum_{j} [x | i][y | j] \= \sum_{x} \sum_{y}\sum_{d | (x, y)} \mu(d) \lfloor\frac{n}{x} \rfloor\lfloor\frac{m}{y} \rfloor\= \sum_{d} \mu(d) \sum_{x}\sum_{y}[d | x][d | y] \lfloor\frac{n}{x} \rfloor\lfloor\frac{m}{y} \rfloor \= \sum_{d} \mu(d) \sum_{x}^{n / d}\lfloor\frac{n}{xd} \rfloor\sum_{y}^{m / d}\lfloor\frac{m}{yd} \rfloor\\]
\(F(n) = \sum_{i = 1}^{n} \sigma_0(i)\)

那么有
\[ Ans = \sum_{d} \mu(d) F(\frac{n}{d})F(\frac{m}{d}) \]
然后线性筛/杜教筛Min25筛洲阁筛筛以下约数个数的前缀和就可以做了.

Codes

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for (;!isdigit(ch); ch = getchar()) if (ch == ‘-‘) flag *= -1;
    for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if (x < 0) putchar(‘-‘), x = -x;
    if (x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}

const int Maxn = 50009;
int prime[Maxn], isnprime[Maxn], mu[Maxn], prefixMu[Maxn], tot, low[Maxn];
LL sigma0[Maxn];
void linearSieve() {
    mu[1] = 1; sigma0[1] = 1;
    rep (i, 2, Maxn - 1) {
        if (!isnprime[i]) mu[i] = -1, prime[++tot] = i, low[i] = i, sigma0[i] = 2;
        for (int k, j = 1; j <= tot && (k = prime[j] * i) < Maxn; ++j) {
            isnprime[k] = 1;
            if (i % prime[j] == 0) {
                mu[k] = 0; low[k] = low[i] * prime[j];
                sigma0[k] = (low[i] == i) ? (sigma0[i] + 1) : (sigma0[k / low[k]] * sigma0[low[k]]);
                break;
            } else {
                mu[k] = -mu[i]; low[k] = prime[j];
                sigma0[k] = sigma0[i] * sigma0[prime[j]];
            }
        }
    }
    rep (i, 1, Maxn - 1) {
        prefixMu[i] = prefixMu[i - 1] + mu[i];
        sigma0[i] += sigma0[i - 1];
    }
}

void init() { linearSieve(); }

void solve() {
    int T = read();
    while (T--) {
        int n = read(), m = read();
        LL ans = 0; int Limit = min(n, m);
        for (int l = 1, r; l <= Limit; l = r + 1) {
            r = min(Limit, min(n / (n / l), m / (m / l)));
            ans += (prefixMu[r] - prefixMu[l - 1] * 1ll) * sigma0[n / l] * sigma0[m / l];
        }
        printf("%lld\n", ans);
    }
}

int main() {
    freopen("BZOJ3994.in", "r", stdin);
    freopen("BZOJ3994.out", "w", stdout);

    init();
    solve();

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}

以上是关于[SDOI2015] 约数个数和的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 3994: [SDOI2015]约数个数和

[BZOI 3994] [SDOI2015]约数个数和

BZOJ3994: [SDOI2015]约数个数和

[SDOI2015]约数个数和

[SDOI2015] 约数个数和 (莫比乌斯反演)

bzoj3994:[SDOI2015]约数个数和