Loading [MathJax]/extensions/tex2jax.js

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}\),然后整除分块搞下复杂度就可以降到很低了。

代码

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// 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;
}
// 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; }
// 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 *