[SDOI 2014] 数表(莫比乌斯反演 + 树状数组) | 错题本

文章目录

题目

[SDOI 2014] 数表

分析

先考虑无 a a a 限制,不妨设 n ≥ m n \geq m nm
∑ i = 1 n ∑ j = 1 m σ ( gcd ⁡ ( i , j ) ) = ∑ d = 1 m σ ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ⁡ ( i , j ) = 1 ] = ∑ d = 1 m σ ( d ) ∑ k = 1 ⌊ m d ⌋ μ ( k ) ⌊ n d k ⌋ ⌊ m d k ⌋ \begin{aligned}&\sum_{i = 1}^n\sum_{j = 1}^m \sigma(\gcd(i, j)) \\ =&\sum_{d = 1}^m \sigma(d)\sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor}[\gcd(i, j)=1] \\=&\sum_{d = 1}^m \sigma(d)\sum_{k=1}^{\left\lfloor\frac{m}{d}\right\rfloor}\mu(k)\left\lfloor\frac{n}{dk}\right\rfloor\left\lfloor\frac{m}{dk}\right\rfloor\end{aligned} ==i=1nj=1mσ(gcd(i,j))d=1mσ(d)i=1dnj=1dm[gcd(i,j)=1]d=1mσ(d)k=1dmμ(k)dkndkm 枚举 T = d k T=dk T=dk ∑ d = 1 m σ ( d ) ∑ k = 1 ⌊ m d ⌋ μ ( k ) ⌊ n d k ⌋ ⌊ m d k ⌋ = ∑ T = 1 m ⌊ n T ⌋ ⌊ m T ⌋ ∑ d ∣ T σ ( d ) μ ( T d ) \begin{aligned}&\sum_{d = 1}^m \sigma(d)\sum_{k=1}^{\left\lfloor\frac{m}{d}\right\rfloor}\mu(k)\left\lfloor\frac{n}{dk}\right\rfloor\left\lfloor\frac{m}{dk}\right\rfloor\\=&\sum_{T=1}^m\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{d|T}\sigma(d)\mu\left(\frac{T}{d}\right)\end{aligned} =d=1mσ(d)k=1dmμ(k)dkndkmT=1mTnTmdTσ(d)μ(dT) f ( x ) = ∑ d ∣ x σ ( d ) μ ( x d ) f(x)=\sum_{d|x}\sigma(d)\mu\left(\frac{x}{d}\right) f(x)=dxσ(d)μ(dx),如果我们知道 f f f 的前缀和,原式就能 O ( m ) O(\sqrt m) O(m ) 求。

接下来考虑 a a a 的限制,即原式中的 σ ( d ) \sigma(d) σ(d) 必须不大于 a a a。不难发现 a a a 的增大会使更多的 σ ( d ) \sigma(d) σ(d) 可以产生贡献,离线询问后对按 a a a 排序,我们枚举这些 σ ( d ) \sigma(d) σ(d) 以及 d d d 的倍数 x x x,并对相应的 f ( x ) f(x) f(x) 进行修改,用树状数组维护即可。

时间复杂度 O ( n ln ⁡ n log ⁡ n + q n log ⁡ n ) O(n\ln n \log n + q \sqrt n \log n) O(nlnnlogn+qn logn)

代码

线性筛积性函数(约数和函数)可见 线性筛 [约数个数/约数和]
2 2 2 的幂次好评。

#include <bits/stdc++.h>

typedef long long LL;

const int MAXN = 100000;
const int MAXQ = 20000;

int Q;
struct Query {
    int N, M, A, i;
} P[MAXQ + 5];
int Ans[MAXN + 5];

bool Comp(Query i, Query j) {
    return i.A < j.A;
}

struct BIT {
    #define lowbit(x) ((x) & (-(x)))

    int A[MAXN + 5];

    void Update(int pos, int d) {
        for (int i = pos; i <= MAXN; i += lowbit(i))
            A[i] += d;
    }

    int GetSum(int pos) {
        int ret = 0;
        for (int i = pos; i > 0; i -= lowbit(i))
            ret += A[i];
        return ret;
    }

    #undef lowbit
} T;

int Mu[MAXN + 5];
LL Sigma[MAXN + 5], Low[MAXN + 5];
bool Vis[MAXN + 5];
std::vector<int> Primes;
std::vector<std::pair<LL, int> > Sig;

void Init(int n) {
    Mu[1] = Sigma[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!Vis[i]) {
            Mu[i] = -1, Sigma[i] = Low[i] = i + 1;
            Primes.push_back(i);
        }
        for (int j = 0, k = 0; j < (int)Primes.size() && i * Primes[j] <= n; j++) {
            Vis[k = i * Primes[j]] = true;
            if (i % Primes[j] == 0) {
                Mu[k] = 0;
                Low[k] = 1 + Low[i] * Primes[j];
                Sigma[k] = Sigma[i] / Low[i] * Low[k];
                break;
            }
            Mu[k] = -Mu[i];
            Low[k] = 1 + Primes[j];
            Sigma[k] = Sigma[i] * Sigma[Primes[j]];
        }
    }
    for (int i = 1; i <= n; i++)
        Sig.push_back(std::make_pair(Sigma[i], i));
    std::sort(Sig.begin(), Sig.end());
}

int Calc(int n, int m) {
    if (n < m) std::swap(n, m);
    int ret = 0;
    for (int lft = 1, rgt = 1; lft <= m; lft = rgt + 1) {
        rgt = std::min(n / (n / lft), m / (m / lft));
        ret += (n / lft) * (m / lft) * (T.GetSum(rgt) - T.GetSum(lft - 1));
    }
    return ret;
}

int main() {
    Init(MAXN);
    scanf("%d", &Q);
    for (int i = 1; i <= Q; i++)
        scanf("%d%d%d", &P[i].N, &P[i].M, &P[i].A), P[i].i = i;
    std::sort(P + 1, P + Q + 1, Comp);
    int cur = 0;
    for (int i = 1; i <= Q; i++) {
        int lim = P[i].A;
        while (cur < (int)Sig.size() && Sig[cur].first <= lim) {
            int d = Sig[cur].second, s = Sig[cur].first;
            for (int j = d; j <= MAXN; j += d)
                T.Update(j, s * Mu[j / d]);
            cur++;
        }
        Ans[P[i].i] = Calc(P[i].N, P[i].M);
    }
    for (int i = 1; i <= Q; i++)
        printf("%d\n", Ans[i] & 2147483647);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值