[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; }