[SDOI2014]数表 莫比乌斯反演+树状数组

题意

求$\sum_{i=1}^n\sum_{j=1}^m[\sigma_1(\gcd(i,j))\le a]\sigma_1(\gcd(i,j))$

Sol

先忽略$a$的限制,按照套路反演

$$\sum_{i=1}^n\sum_{j=1}^m\sigma_1(\gcd(i,j))$$

枚举$\gcd(i,j)$

$$=\sum_{d=1}^{n}\sigma_1(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{j=1}^{\lfloor\frac md\rfloor}[\gcd(i,j)=1]$$

$$=\sum_{d=1}^n\sigma_1(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{j=1}^{\lfloor\frac md\rfloor}\sum_{k|\gcd(i,j)}\mu(k)$$

枚举$k$

$$=\sum_{d=1}^n\sigma_1(d)\sum_{k=1}^{\lfloor\frac nd\rfloor}\mu(k)\lfloor\frac n{dk}\rfloor\lfloor\frac m{dk}\rfloor$$

设$dk=T$,枚举$T$

$$=\sum_{T=1}^n\lfloor\frac n{T}\rfloor\lfloor\frac m{T}\rfloor\sum_{d|T}\sigma_1(d)\mu(\frac Td)$$

如果没有$a$的限制就可以$O(\sqrt n)$求了

考虑将所有询问按照$a$排序,然后预处理出所有$\sigma_1(n)$的值,排序,当询问的$a\ge\sigma_1(i)$时插入$i$以及其倍数的$\sigma_1(d)\mu(\frac Td)$

单点修改+单点查询,可以使用树状数组维护

时间复杂度$O(Q\sqrt n\log n)$

#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
int Read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    }
    return x * f;
}
int mu[100005], d[100005], sum[100005], vis[100005], prime[100005], ans[100005], c[100005], tot = 0, pos = 1;
pair<int, int> f[100005];
void prework(int n)
{
    mu[1] = d[1] = sum[1] = 1;
    f[1] = make_pair(1, 1);
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i])
        {
            prime[++tot] = i;
            mu[i] = -1;
            d[i] = sum[i] = i + 1;
            f[i] = make_pair(d[i], i);
        }
        for (int j = 1; j <= tot && i * prime[j] <= n; j++)
        {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                d[i * prime[j]] = d[i] / sum[i] * (sum[i] * prime[j] + 1);
                sum[i * prime[j]] = sum[i] * prime[j] + 1;
                f[i * prime[j]] = make_pair(d[i * prime[j]], i * prime[j]);
                break;
            }
            mu[i * prime[j]] = mu[i] * mu[prime[j]];
            d[i * prime[j]] = d[i] * d[prime[j]];
            sum[i * prime[j]] = 1 + prime[j];
            f[i * prime[j]] = make_pair(d[i * prime[j]], i * prime[j]);
        }
    }
}
struct que
{
    int n, m, a, id;
} q[500005];
bool cmp(que A, que B)
{
    return A.a < B.a;
}
int lowbit(int x) { return x & (-x); }
void modify(int pos, int val)
{
    for (; pos <= 100000; pos += lowbit(pos))
        c[pos] += val;
}
int query(int pos)
{
    int ans = 0;
    for (; pos >= 1; pos -= lowbit(pos))
        ans += c[pos];
    return ans;
}
int solve(int x, int y)
{
    int ans = 0;
    if (x > y)
        swap(x, y);
    for (int l = 1, r; l <= x; l = r + 1)
    {
        r = min(x / (x / l), y / (y / l));
        ans += (query(r) - query(l - 1)) * (x / l) * (y / l);
    }
    return ans;
}
signed main()
{
    prework(100000);
    sort(f + 1, f + 100000 + 1);
    int T = Read();
    for (int i = 1; i <= T; i++)
    {
        q[i].n = Read(), q[i].m = Read(), q[i].a = Read();
        q[i].id = i;
    }
    sort(q + 1, q + T + 1, cmp);
    for (int i = 1; i <= T; i++)
    {
        while (f[pos].first <= q[i].a && pos <= 100000)
        {
            for (int k = f[pos].second; k <= 100000; k += f[pos].second)
            {
                modify(k, f[pos].first * mu[k / f[pos].second]);
            }
            ++pos;
        }
        ans[q[i].id] = solve(q[i].m, q[i].n);
    }
    for (int i = 1; i <= T; i++)
        cout << (ans[i] & (~(1 << 31))) << endl;
}
Code
posted @ 2020-06-02 14:16  verjun  阅读(140)  评论(0编辑  收藏  举报