「Bzoj 3529」「Sdoi2014」数表 (莫比乌斯反演+离线+树状数组)

BZOJ 3529
题意:给定多组$n,m,a$,设$d_1(n)$为$n$约数和,求
$$
\sum_{i=1}^n \sum_{j=1}^m d_1(\gcd(i, j))
$$
并且满足$d_1(\gcd(i, j)) \leq a$

不考虑$a$的限制,枚举约数
$$
\sum_{d=1}^n \sum_{i=1}^n \sum_{j=1}^m d_1(d)[gcd(i,j)=d]
$$
发现$d_1(d)$与$i,j$无关,移到前面
$$
\sum_{d=1}^n d_1(d) \sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=d]
$$
将后面用反演套路化简,得
$$
\sum_{d=1}^n d_1(d) \sum_{k=1}^{\lfloor \frac nd \rfloor} \mu(k) \lfloor \frac n{kd} \rfloor \lfloor \frac m{kd} \rfloor
$$
设$T=kd$,则
$$
\sum_{T=1}^n \lfloor \frac nT \rfloor \lfloor \frac mT \rfloor \sum_{d|T}d_1(d) \mu(\frac Td)
$$
如果没有$a$的限制,则线性筛后面的函数即可。
考虑$a$的限制。
只有$d_1(x) \leq a$的才会有贡献。
所以将$d_1(x)$从小到大排序,将询问离线后按$a$从小到大排序
每次按照$a$的单增补齐到$a$,补齐即枚举$x$的倍数进行增加。
维护一个单调修改区间查值的数据结构,用树状数组维护之。
时间复杂度:$O(n\log ^2 n + q \sqrt n \log n)$

知识点:
1、枚举因数非常常用

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<cmath>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db long double
#define fir first
#define sec second
#define mp make_pair
using namespace std;

namespace flyinthesky {

    const int MAXN = 100000 + 5;

    struct data {
        int n, m, a, id;
        bool operator < (const data &rhs) const {return a < rhs.a;}
    }qry[20000 + 5];

    int mu[MAXN], pri[MAXN], vis[MAXN], tot;
    int e[MAXN], ans[MAXN];
    pair<int, int > d1[MAXN];
    int Q;

    int a[MAXN];

    int min(int x, int y) {return x < y ? x : y;}
    int lowbit(int x) {return x & (-x);}
    int query(int x) {
        int ret = 0;
        for (int i = x; i; i -= lowbit(i)) ret += a[i];
        return ret;
    }
    void add(int x, int v) {
        for (int i = x; i <= 100000; i += lowbit(i)) a[i] += v;
    }

    int cal(int n, int m) {
        int l = 1, ret = 0;
        while (l <= n) {
            int r = min(n / (n / l), m / (m / l));
            ret = ret + (n / l) * (m / l) * (query(r) - query(l - 1));
            l = r + 1;
        }
        /*for (LL d = 1; d <= n; ++d) {
            ret = (ret + (n / d) * (m / d) % MO * (query(d) - query(d - 1) + MO) % MO) % MO;
        }*/
        return ret;
    }

    void init() {
        ms(mu, 0), ms(pri, 0), ms(vis, 0), ms(e, 0), tot = 0;
        mu[1] = 1, d1[1] = make_pair(1, 1), e[1] = 1;
        for (int i = 2; i <= 100000; ++i) {
            if (!vis[i]) pri[++tot] = i, d1[i] = make_pair(i + 1, i), e[i] = i + 1, mu[i] = -1;
            for (int j = 1; j <= tot && (LL)i * pri[j] <= 100000ll; ++j) {
                vis[i * pri[j]] = 1;
                if (i % pri[j] == 0) {
                    mu[i * pri[j]] = 0;
                    d1[i * pri[j]] = make_pair(d1[i].first / e[i] * (e[i] * pri[j] + 1), i * pri[j]);
                    e[i * pri[j]] = e[i] * pri[j] + 1;
                    break ;
                } else {
                    mu[i * pri[j]] = mu[i] * mu[pri[j]];
                    d1[i * pri[j]] = make_pair(d1[i].first * d1[pri[j]].first, i * pri[j]);
                    e[i * pri[j]] = pri[j] + 1;
                }
            }
        }
    }
    void clean() {
        ms(a, 0);
    }
    int solve() {

        clean(); 
        scanf("%d", &Q);
        for (int i = 1; i <= Q; ++i) {
            scanf("%d%d%d", &qry[i].n, &qry[i].m, &qry[i].a);
            if (qry[i].n > qry[i].m) swap(qry[i].n, qry[i].m);
            qry[i].id = i;
        }
        sort(qry + 1, qry + 1 + Q);
        sort(d1 + 1, d1 + 1 + 100000);

        int now = 1;
        for (int i = 1; i <= Q; ++i) {
            while (d1[now].first <= qry[i].a) {
                for (int j = d1[now].second; j <= 100000; j += d1[now].second) 
                    add(j, (d1[now].first * mu[j / d1[now].second]));
                ++now;
            }
            ans[qry[i].id] = cal(qry[i].n, qry[i].m);
        }

        for (int i = 1; i <= Q; ++i) printf("%d\n", ans[i] & (~(1 << 31)));

        return 0;
    }
}
int main() {
    flyinthesky::init();
    flyinthesky::solve();
    return 0;
}
------ 本文结束 ------