P3327:「SDOI2015」约数个数和题解

主要思路和推导

这道题还是比较有意思的(啊呀看了好久的题解才会写,真菜),主要就是把\(d\)转换成了:

\[ d(xy) = \sum_{i|x}^x \sum_{j|y}^y [\gcd(i,j) = 1] \]

可以感性理解一下:每一次约数被枚举出来的时候,只有互质的情况下才会被计数,避免了重复计数。

所以我们可以试着化简一下:

\[ \sum^N_{i=1} \sum^M_{j=1} d(i,j) = \sum^N_{i=1} \sum^M_{j=1} \sum_{x|i}^i \sum_{y|j}^j [\gcd(x,y) = 1] \\ \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [\gcd(x,y) = 1] \]

可以进行莫比乌斯反演了:

\[f(h) = \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [\gcd(x,y) = h] \\ F(h) = \sum_{h|d} f(h) \\ F(h) = \sum_{h|d} \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [\gcd(x,y) = d] \\ F(h) = \sum^N_{i=1} \sum^M_{j=1} \lfloor \frac{N}{i} \rfloor \lfloor \frac{M}{j} \rfloor [h|\gcd(x,y)] \\ F(h) = \sum^{\frac{N}{h}}_{i=1} \sum^{\frac{M}{h}}_{j=1} \lfloor \frac{N}{hi} \rfloor \lfloor \frac{M}{hj} \rfloor \\ f(h)=\sum_{h|d} \mu(\frac{d}{h})F(h) \\ f(h)=\sum_{h|d} \mu(\frac{d}{h}) \sum^{\frac{N}{h}}_{i=1} \sum^{\frac{M}{h}}_{j=1} \lfloor \frac{N}{hi} \rfloor \lfloor \frac{M}{hj} \rfloor \\ f(h)=\sum_{h|d} \mu(\frac{d}{h}) (\sum^{\frac{N}{h}}_{i=1} \lfloor \frac{N}{hi} \rfloor) (\sum^{\frac{M}{h}}_{j=1} \lfloor \frac{M}{hj} \rfloor) \]

后面的分数部分我们直接设个函数预处理即可:

\[ \sum^{\frac{N}{h}}_{i=1} \lfloor \frac{N}{hi} \rfloor = \sum^{\frac{N}{h}}_{i=1} \lfloor \frac{N}{h} * \frac{1}{i} \rfloor \]

可见得我们把\( \frac{N}{h} \)作为\(x\),预处理出\(s(x) = \sum_{i=1}^x \frac{x}{i}\),然后整除分块搞下复杂度就可以降到很低了。

代码

// P3327.cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX_N = 50100;
int n, prime[MAX_N], tot, mu[MAX_N], muprefix[MAX_N];
ll g[MAX_N];
bool vis[MAX_N];
int main()
{
    scanf("%d", &n);
    mu[1] = 1;
    for (int i = 2; i < MAX_N; i++)
    {
        if (!vis[i])
            prime[++tot] = i, mu[i] = -1;
        for (int j = 1; j <= tot && i * prime[j] < MAX_N; j++)
        {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0)
                break;
            else
                mu[i * prime[j]] = -mu[i];
        }
    }
    for (int i = 1; i < MAX_N; i++)
        muprefix[i] = muprefix[i - 1] + mu[i];
    for (int i = 1; i < MAX_N; i++)
    {
        ll ans = 0;
        for (int l = 1, r = 0; l <= i; l = r + 1)
        {
            r = (i / (i / l));
            ans += 1LL * (r - l + 1) * 1LL * (i / l);
        }
        g[i] = ans;
    }
    while (n--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        ll ans = 0;
        for (int l = 1, r; l <= min(a, b); l = r + 1)
        {
            r = min(a / (a / l), b / (b / l));
            ans += (muprefix[r] - muprefix[l - 1]) * 1LL * g[a / l] * 1LL * g[b / l];
        }
        printf("%lld\n", ans);
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *