題目鏈接
LOJ2476:https://loj.ac/problem/2476
LOJ2565:https://loj.ac/problem/2565
題解
參考照搬了 wxh 的博客。
為了方便,下文用 \((x, y)\) 表示 \({\rm gcd}(x, y)\)。
先分析 LOJ2476。
注意到對於任意一個數組 \(a\),第 \(x\) 項的值 \(a_x\) 可以展開寫成 \(\sum_\limits{i = 1}^{x} a_i[i = x]\),進一步地,有:
對於數組 \(a\),不妨設 \(f(a) = a * \mu\),那么原式等於:
令 \(g(a)(x) = \sum_\limits{x | i}a_i\),原式等於:
當 \(d\) 已知時,令 \(P_i = f(E)(id), Q_i = f(F)(id), R_i = g(C)(id), S_i = A_{id}, T_i = B_{id}, W_i = D_{id}\)。設 \(m = \left\lfloor\frac{n}{d}\right\rfloor\),原式等於:
對於每個 \(d\),我們可以先預處理出數組 \(P, Q, R, S, T, W\)。這樣,如果 \(x\) 與 \(y\) 也已知,那么 \(P_xQ_yR_{xy}\) 可以直接得到。考慮如何求后面的部分:
設 \(u = ix, v= jy\),那么上式等於:
令 \(h(a)(x) = \sum_\limits{i | x} a_i\),那么上式可以從右往左依次計算,具體地,令函數 \(f_1(u) = [x | u]S_{u}\),那么我們可以先計算 \(g(f_1)\) 來得到每一個 \(r\) 對應的 \(\sum_\limits{r | u}[x | u]S_{u}\),再令函數 \(f_2(r) = f(W)(r) \times g(f_1)(r)\),那么我們可以計算 \(h(f_2)\) 來得到每一個 \(v\) 對應的 \(\sum_\limits{r | v} f(W)(r) \sum_\limits{r | u} [x | u]S_{u}\)。這樣,上式即為枚舉所有 \(y\) 的倍數 \(v\),求 \(T_{v} \times h(f_2)(v)\)。
先忽略所有求 \(f(a), g(a), h(a)\) 等函數的復雜度。首先我們需要枚舉 \(d\)。對於每一個 \(d\),我們需要暴力枚舉所有可能的 \(x, y\),對於每一組合法的 \(x, y\)(即滿足 \(xy \leq m, (x, y) = 1\)),再暴力枚舉不超過 \(m\) 的 \(y\) 的倍數。經程序驗證,當 \(n = 10^5\) 時,總枚舉量為 \(98380871\),在可接受的范圍內。
對於一個長度為 \(n\) 的數組 \(a\),求 \(f(a)\) 的時間復雜度為 \(O(n \log n)\),求 \(g(a)\) 與 \(h(a)\) 的時間復雜度均為 \(O(n \log \log n)\)。首先,我們需要預處理出 \(f(E), f(F)\) 與 \(g(C)\),時間復雜度為 \(O(n \log n)\)。 對於每一個 \(d\),我們需要求出 \(P, Q, R, S, T, W\) 與 \(f(W)\),時間復雜度為 \(O(m \log m)\)。當 \(d\) 確定時,對於每一個 \(x\),我們需要求出 \(f_1, g(f_1), f_2, h(f_2)\),由於 \(x \leq \sqrt m\),因此時間復雜度為 \(O(m \sqrt m \log \log m)\)。
最終時間復雜度為 \(O(n \log n)+ \sum_\limits{i = 1}^n O\left( \left(\frac{n}{i}\right) \log \left(\frac{n}{i}\right) + \left(\frac{n}{i}\right)^{1.5} \log \log\left(\frac{n}{i}\right)\right)\) \(= O(n \sqrt n \log \log n)\)。
接下來分析 LOJ2565。
首先有 \(d(ijk) = \sum_\limits{x |i}\sum_\limits{y | j}\sum_\limits{z | k}[(x, y) = 1][(y, z) = 1][(x, z) = 1]\)。證明如下:
令 \(s = ijk\)。分別寫出 \(i, j, k, s\) 的唯一分解式:
\[\begin{aligned}i &= p_1^{a_1}p_2^{a_2} \cdots p_t^{a_t} \\j &= p_1^{b_1}p_2^{b_2} \cdots p_t^{b_t} \\ k &= p_1^{c_1}p_2^{c_2} \cdots p_t^{c_t} \\ s &= p_1^{a_1 + b_1 + c_1}p_2^{a_2 + b_2 + c_2} \cdots p_t^{a_t + b_t + c_t}\end{aligned} \]那么 \(d(ijk) = \prod_\limits{r = 1}^t(a_r + b_r + c_r + 1)\)。
考慮計算 \(\sum_\limits{x |i}\sum_\limits{y | j}\sum_\limits{z | k}[(x, y) = 1][(y, z) = 1][(x, z) = 1]\) 的值。不難發現,滿足 \(x | i, y | j, z | k\) 且 \((x, y) = 1, (y, z) = 1, (x, z) = 1\) 的 \(x, y, z\) 必然有:\(\forall r(1 \leq r \leq t)\),\(x, y, z\) 中至少有兩個數滿足其唯一分解式中 \(p_r\) 的指數為 \(0\)。當 \(x, y, z\) 中至少有兩個數滿足其唯一分解式中 \(p_r\) 的指數為 \(0\) 時,\(x, y, z\) 三個數的唯一分解式中 \(p_r\) 的指數共有 \(a_r + b_r + c_r + 1\) 種情況。顯然,對於各個 \(r\),情況是獨立的,因此根據乘法原理,滿足 \(x | i, y | j, z | k\) 且 \((x, y) = 1, (y, z) = 1, (x, z) = 1\) 的 \(x,y, z\) 共有 \(\prod_\limits{r = 1}^t (a_r + b_r + c_r + 1)\) 組。其數量恰好等於 \(d(ijk)\) 的值。
將 \(d(ijk) = \sum_\limits{x |i}\sum_\limits{y | j}\sum_\limits{z | k}[(x, y) = 1][(y, z) = 1][(x, z) = 1]\) 代入原式得:
定義函數 \(a_i = \left\lfloor\frac{A}{i}\right\rfloor, b_i = \left\lfloor\frac{B}{i}\right\rfloor, c_i = \left\lfloor\frac{C}{i}\right\rfloor, f_i = [i = 1]\)。那么原式即為:
至此,我們得到了和上題形式完全一樣的式子。因此直接套用上題做法即可。單組數據的時間復雜度不變。不過有點卡常,因為本題的正解壓根就不是這個。
代碼
LOJ2476 代碼如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
unsigned long long a[N], b[N], c[N], d[N], e[N], f[N], p[N], q[N], r[N], s[N], t[N], w[N], u[N], v[N];
int n, primes[N], all;
bool is_prime[N];
void F(unsigned long long* a, int n) {
for (int i = 1; i <= n; ++i) {
for (int j = i + i; j <= n; j += i) {
a[j] -= a[i];
}
}
}
void G(unsigned long long* a, int n) {
for (int i = 1; i <= all && primes[i] <= n; ++i) {
for (int j = n / primes[i]; j; --j) {
a[j] += a[j * primes[i]];
}
}
}
void H(unsigned long long* a, int n) {
for (int i = 1; i <= all && primes[i] <= n; ++i) {
for (int j = 1; j * primes[i] <= n; ++j) {
a[j * primes[i]] += a[j];
}
}
}
void sieve(int n) {
fill(is_prime + 1, is_prime + 1 + n, true);
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
primes[++all] = i;
}
for (int j = 1; j <= all; ++j) {
if (primes[j] * i > n) {
break;
}
is_prime[primes[j] * i] = false;
if (i % primes[j] == 0) {
break;
}
}
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%llu", &a[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%llu", &b[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%llu", &c[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%llu", &d[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%llu", &e[i]);
}
for (int i = 1; i <= n; ++i) {
scanf("%llu", &f[i]);
}
sieve(n);
F(e, n);
F(f, n);
G(c, n);
unsigned long long answer = 0;
for (int i = 1; i <= n; ++i) {
int m = n / i;
for (int j = 1; j <= m; ++j) {
p[j] = e[j * i];
q[j] = f[j * i];
r[j] = c[j * i];
s[j] = a[j * i];
t[j] = b[j * i];
w[j] = d[j * i];
}
F(w, m);
for (int x = 1; x * x <= m; ++x) {
fill(u + 1, u + 1 + m, 0);
fill(v + 1, v + 1 + m, 0);
for (int j = x; j <= m; j += x) {
u[j] = s[j], v[j] = t[j];
}
G(u, m);
G(v, m);
for (int j = 1; j <= m; ++j) {
u[j] *= w[j];
v[j] *= w[j];
}
H(u, m);
H(v, m);
for (int j = 1; j <= m; ++j) {
u[j] *= t[j];
v[j] *= s[j];
}
for (int y = x; x * y <= m; ++y) {
if (__gcd(x, y) == 1) {
unsigned long long s1 = 0, s2 = 0;
for (int j = y; j <= m; j += y) {
s1 += u[j];
s2 += v[j];
}
answer += s1 * p[x] * q[y] * r[x * y];
if (x != y) {
answer += s2 * p[y] * q[x] * r[x * y];
}
}
}
}
}
printf("%llu\n", answer);
return 0;
}
LOJ2565 代碼如下:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, mod = 1e9 + 7;
void add(int& x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
}
void sub(int& x, int y) {
x -= y;
if (x < 0) {
x += mod;
}
}
template<typename T>
int mul(T x) {
return x;
}
template<typename T, typename... R>
int mul(T x, R... y) {
return (long long) x * mul(y...) % mod;
}
int tt, A, B, C, a[N], b[N], c[N], d[N], e[N], f[N], p[N], q[N], r[N], s[N], t[N], w[N], u[N], v[N], primes[N], all;
bool is_prime[N];
void F(int* a, int n) {
for (int i = 1; i <= n; ++i) {
for (int j = i + i; j <= n; j += i) {
sub(a[j], a[i]);
}
}
}
void G(int* a, int n) {
for (int i = 1; i <= all && primes[i] <= n; ++i) {
for (int j = n / primes[i]; j; --j) {
add(a[j], a[j * primes[i]]);
}
}
}
void H(int* a, int n) {
for (int i = 1; i <= all && primes[i] <= n; ++i) {
for (int j = 1; j * primes[i] <= n; ++j) {
add(a[j * primes[i]], a[j]);
}
}
}
void sieve(int n) {
fill(is_prime + 1, is_prime + 1 + n, true);
all = 0;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
primes[++all] = i;
}
for (int j = 1; j <= all; ++j) {
if (primes[j] * i > n) {
break;
}
is_prime[primes[j] * i] = false;
if (i % primes[j] == 0) {
break;
}
}
}
}
int main() {
scanf("%d", &tt);
while (tt--) {
scanf("%d%d%d", &A, &B, &C);
int n = max(A, max(B, C));
sieve(n);
for (int i = 1; i <= n; ++i) {
a[i] = A / i;
b[i] = B / i;
c[i] = C / i;
d[i] = e[i] = f[i] = (i == 1);
}
F(e, n);
F(f, n);
G(c, n);
int answer = 0;
for (int i = 1; i <= n; ++i) {
int m = n / i;
for (int j = 1; j <= m; ++j) {
p[j] = e[j * i];
q[j] = f[j * i];
r[j] = c[j * i];
s[j] = a[j * i];
t[j] = b[j * i];
w[j] = d[j * i];
}
F(w, m);
for (int x = 1; x * x <= m; ++x) {
fill(u + 1, u + 1 + m, 0);
fill(v + 1, v + 1 + m, 0);
for (int j = x; j <= m; j += x) {
u[j] = s[j], v[j] = t[j];
}
G(u, m);
G(v, m);
for (int j = 1; j <= m; ++j) {
u[j] = mul(u[j], w[j]);
v[j] = mul(v[j], w[j]);
}
H(u, m);
H(v, m);
for (int j = 1; j <= m; ++j) {
u[j] = mul(u[j], t[j]);
v[j] = mul(v[j], s[j]);
}
for (int y = x; x * y <= m; ++y) {
if (__gcd(x, y) == 1) {
int s1 = 0, s2 = 0;
for (int j = y; j <= m; j += y) {
add(s1, u[j]);
add(s2, v[j]);
}
add(answer, mul(s1, p[x], q[y], r[x * y]));
if (x != y) {
add(answer, mul(s2, p[y], q[x], r[x * y]));
}
}
}
}
}
printf("%d\n", answer);
}
return 0;
}
