题意

luogu - P4619 [SDOI2018]旧试题

i=1Aj=1Bk=1Cσ0(ijk)\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C \sigma_0(ijk)

σ0(x)\sigma_0(x)xx 约数个数。

多测 T10T\le 101max(A,B,C)2×2×1051\le \sum \max(A,B,C)\le 2\times 2\times 10^5

题解

下文简记 gcd(i,j)=(i,j)\gcd(i,j)=(i,j)lcm(i,j)=[i,j]\operatorname{lcm}(i,j)=[i,j]

显然有结论,就当作 σ0\sigma_0 的性质吧。

σ0(ijk)=xiyjzk[(x,y)=1][(x,y)=1][(y,z)=1]\sigma_0(ijk)=\sum_{x|i}\sum_{y|j}\sum_{z|k} [(x,y)=1][(x,y)=1][(y,z)=1]

代回原式,按照套路写成 ϵ\epsilon

i=1Aj=1Bk=1Cxiyjzkϵ((x,y))ϵ((x,y))ϵ((y,z))\sum_{i=1}^A\sum_{j=1}^B\sum_{k=1}^C \sum_{x|i}\sum_{y|j}\sum_{z|k} \epsilon((x,y))\epsilon((x,y))\epsilon((y,z))

反演,

i=1Axij=1Byjk=1Ckzd1x,d1yμ(d2)d2x,d2zμ(d2)d3y,d3zμ(d3)\sum_{i=1}^A\sum_{x|i}\sum_{j=1}^B\sum_{y|j}\sum_{k=1}^C\sum_{k|z} \sum_{d_1|x,d_1|y} \mu(d_2)\sum_{d_2|x,d_2|z} \mu(d_2)\sum_{d_3|y,d_3|z} \mu(d_3)\\

显然有 d1i,d1j,d2i,d2k,d3j,d3kd_1|i,d_1|j,d_2|i,d_2|k,d_3|j,d_3|k,即 [d1,d2]i,[d2,d3]j,[d1,d3]k[d_1,d_2]\mid i,\,[d_2,d_3]\mid j,\,[d_1,d_3]\mid k
交换求和顺序,i,j,ki',j',k' 分别表示为 [d1,d2],[d2,d3],[d1,d3][d_1,d_2],[d_2,d_3],[d_1,d_3] 的几倍。
再考虑 xi\sum_{x\mid i}, 因为有 xi,d1x,d2xx\mid i,\,d_1\mid x,\,d_2\mid x,所以 xx[d1,d2][d_1,d_2] 的倍数,且为 xxii 的因数,这样的 xxσ0(i/[d1,d2])=σ0(i)\sigma_0(i/[d_1,d_2])=\sigma_0(i') 个。y,zy,z 同理。

d1=1min(A,B)d2=1min(A,C)d3=1min(B,C)μ(d2)μ(d1)μ(d3)i=1A/[d1,d2]σ0(i)j=1B/[d1,d3]σ0(j)k=1C/[d2,d3]σ0(k)\sum_{d_1=1}^{\min(A,B)} \sum_{d_2=1}^{\min(A,C)} \sum_{d_3=1}^{\min(B,C)}\mu(d_2)\mu(d_1)\mu(d_3) \sum_{i'=1}^{\left\lfloor A/[d_1,d_2]\right\rfloor} \sigma_0\left(i'\right) \sum_{j'=1}^{\left\lfloor B/[d_1,d_3]\right\rfloor} \sigma_0\left(j'\right) \sum_{k'=1}^{\left\lfloor C/[d_2,d_3]\right\rfloor} \sigma_0\left(k'\right)

考虑:

f(n)=i=1nσ0(i)=i=1nd=1n[dn]=d=1nnd\begin{aligned} f(n)=&\sum_{i=1}^{n} \sigma_0\left(i\right)\\ =&\sum_{i=1}^{n} \sum_{d=1}^{n} [d\mid n]\\ =&\sum_{d=1}^{n} \left\lfloor \frac{n}{d}\right\rfloor \end{aligned}

f(n)f(n) 可以单次 O(n)O(\sqrt{n}) 求;考虑实际意义的话 f(n)f(n) 为 除数函数(即约数个数)前缀和,可以 O(n)O(n) 预处理。

代回原式:

d1=1min(A,B)d2=1min(A,C)d3=1min(B,C)μ(d1)μ(d2)μ(d3)f(A[d1,d2])f(B[d1,d3])f(C[d2,d3])\sum_{d_1=1}^{\min(A,B)} \sum_{d_2=1}^{\min(A,C)}\sum_{d_3=1}^{\min(B,C)}\mu(d_1)\mu(d_2)\mu(d_3)\cdot f\left(\left\lfloor \frac{A}{[d_1,d_2]}\right\rfloor\right) f\left(\left\lfloor \frac{B}{[d_1,d_3]}\right\rfloor\right) f\left(\left\lfloor \frac{C}{[d_2,d_3]}\right\rfloor\right)

搞了半天还是 O(n3)O(n^3) 的复杂度,一分没有

考虑到我们算的是有序对 (d1,d2,d3)(d_1,d_2,d_3) 对答案的贡献,然后就 莫名其妙 地想到转化成图上三元环计数。具体的:

  • 每个点有点权,记 valu=μ(u)val_u=\mu(u)
  • (u,v)(u,v) 之间连边,边权 wu,v=[u,v]w_{u,v}=[u,v]
  • 对于一个有序对 (u,v,w)(u,v,w) 若两两不同则可以表示成图上一个三元环
  • 一个三元环 (u,v,w)(u,v,w) 有贡献当且仅当 umin(A,B)vmin(A,C)wmin(B,C)u\le\min(A,B)\wedge v\le \min(A,C)\wedge w\le \min(B,C),且其贡献为 valuvalvvalwf(Awu,v)f(Bwu,w)f(Cwv,w)\displaystyle val_uval_vval_w\cdot f\left(\left\lfloor \frac{A}{w_{u,v}}\right\rfloor\right)f\left(\left\lfloor \frac{B}{w_{u,w}}\right\rfloor\right)f\left(\left\lfloor \frac{C}{w_{v,w}}\right\rfloor\right)

考虑把一些没必要的点、边删掉,删除 valu=0val_u=0 的点,和 wu,v>max(A,B,C)w_{u,v}>\max(A,B,C) 的边 (u,v)(u,v)

建图
枚举边权 ww,边 (u,v)(u,v) 满足 [u,v]=w,μ(u)0,μ(v)0[u,v]=w,\mu(u)\ne 0,\mu(v)\ne 0u,vu,v 不能有平方因子,那么 ww 也不能有,对 ww 质因数分解,w=p1p2prw=p_1p_2\dots p_r 需要满足 p\forall ppup\mid upvp\mid v。预处理因子,枚举子集即可。

实测 A,B,C=105A,B,C=10^5 时边数只有 760741760741

三元环计数有 O(mm)O(m\sqrt{m}) 的方法(mm 为边数,假设 n,mn,m 同阶):

首先要对所有的无向边进行定向:对于任何一条边,从度数大的点连向度数小的点,如果度数相同,从编号小的点连向编号大的点。此时这张图是一个有向无环图
之后枚举每一个点 uu,然后将 uu 的所有相邻的点都标记上 「被 uu 访问了」,然后再枚举 uu 的相邻的点 vv,然后再枚举 vv 的相邻的点 ww,如果 ww 存在「被 uu 访问了」的标记,那么 (u,v,w)(u,v,w) 就是一个三元环了。
而且每个三元环只会被计算一次



注意我们枚举到的三元环是无序三元组,而统计的是有序三元组,还要手动枚举 66 种情况。

注意还有 (d1,d2,d3)(d_1,d_2,d_3) 其中两个相同的情况,这是 O(m)O(m) 的,还有三个都相等的情况这是 O(n)O(n) 的。

CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 200010;
const int mod = 1e9 + 7;
int gcd(int a, int b) {
return !b ? a : gcd(b, a % b);
}
int p[N], mu[N], c[N], tot, f[N]; // f[i] 约数个数前缀和
vector<int> divs[N];
vector<pii> e[N];
struct edge {int u, v, w;} E[2000000];
pii vis[N];
int deg[N];
int n, T, A, B, C, totE;
bool v[N];
void init(int n) {
mu[1] = 1; f[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!v[i]) p[++tot] = i, mu[i] = -1, c[i] = 1, f[i] = 2, divs[i].push_back(i);
for (int j = 1; j <= tot && p[j] * i <= n; ++j) {
v[i * p[j]] = 1;
divs[i * p[j]] = divs[i];
if (i % p[j] == 0) {
mu[i * p[j]] = 0;
c[i * p[j]] = c[i] + 1;
f[i * p[j]] = f[i] / (c[i] + 1) * (c[i] + 2);
break;
}
divs[i * p[j]].push_back(p[j]);
mu[i * p[j]] = -mu[i];
f[i * p[j]] = f[i] * 2;
c[i * p[j]] = 1;
}
}
for (int i = 1; i <= n; ++i) f[i] = (f[i] + f[i - 1]) % mod;
}
ll calc(int u, int v, int w, int uv, int uw, int vw) {
if (u > min(A, B) || v > min(A, C) || w > min(B, C)) return 0;
int t = mu[u] * mu[v] * mu[w];
ll r = (ll)f[A / uv] * f[B / uw] % mod * f[C / vw] % mod;
return t == 1 ? r : mod - r;
}
void solve() {
totE = 0;
for (int i = 1; i <= n; ++i) e[i].clear(), deg[i] = 0, vis[i] = pii(0, 0);
for (int k = 1; k <= n; ++k) if (mu[k] != 0) {
int c = divs[k].size(), ful = (1 << c) - 1;
vector<int> id(ful + 1, 0);
for (int sub = 0; sub <= ful; ++sub) {
int u = 1;
for (int i = 0; i < c; ++i) if ((sub >> i) & 1) u *= divs[k][i];
id[sub] = u;
}
for (int sub = 0; sub <= ful; ++sub) {
for (int ssub = sub; true; ssub = (ssub - 1) & sub) {
int u = id[sub], v = id[ssub ^ ful];
if (u < v) {
E[++totE] = edge{u, v, k};
++deg[u], ++deg[v];
}
if(ssub == 0) break;
}
}
}
ll ans = 0;
for (int i = 1; i <= totE; ++i) {
int u = E[i].u, v = E[i].v, w = E[i].w;
ans = (ans + calc(u, u, v, u, w, w)) % mod;
ans = (ans + calc(u, v, u, w, u, w)) % mod;
ans = (ans + calc(v, u, u, w, w, u)) % mod;
ans = (ans + calc(v, v, u, v, w, w)) % mod;
ans = (ans + calc(v, u, v, w, v, w)) % mod;
ans = (ans + calc(u, v, v, w, w, v)) % mod;
}
for (int u = 1; u <= n; ++u) if (mu[u] != 0) {
ans = (ans + calc(u, u, u, u, u, u)) % mod;
}
for (int i = 1; i <= totE; ++i) {
if (deg[E[i].u] < deg[E[i].v] || (deg[E[i].u] == deg[E[i].v] && E[i].u < E[i].v))
swap(E[i].u, E[i].v);
e[E[i].u].push_back(pii(E[i].v, E[i].w));
}
for (int u = 1; u <= n; ++u) {
for (int i = 0; i < (int)e[u].size(); ++i)
vis[e[u][i].first] = pii(u, e[u][i].second);
for (int i = 0, v; i < (int)e[u].size(); ++i) {
v = e[u][i].first;
for (int j = 0, w; j < (int)e[v].size(); ++j) {
w = e[v][j].first;
if (vis[w].first != u) continue;
int uv = e[u][i].second, vw = e[v][j].second, uw = vis[w].second;
ans = (ans + calc(u, v, w, uv, uw, vw)) % mod;
ans = (ans + calc(u, w, v, uw, uv, vw)) % mod;
ans = (ans + calc(v, u, w, uv, vw, uw)) % mod;
ans = (ans + calc(v, w, u, vw, uv, uw)) % mod;
ans = (ans + calc(w, u, v, uw, vw, uv)) % mod;
ans = (ans + calc(w, v, u, vw, uw, uv)) % mod;
}
}
}
cout << ans << endl;
}
void rmain() {
cin >> A >> B >> C;
n = max(max(A, B), C);
solve();
}
int main() {
init(N - 1);
cin >> T;
while (T--) rmain();
return 0;
}