LOJ2476. 「2018 集訓隊互測 Day 3」蒜頭的獎杯 & LOJ2565. 「SDOI2018」舊試題(莫比烏斯反演)


題目鏈接

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]\),進一步地,有:

\[\begin{aligned}a_x &= \sum_{i = 1}^{x} a_i [i = x] \\ &= \sum_{i | x}a_i[\frac{x}{i} = 1] \\ &= \sum_{i |x} a_i \sum_{p | \frac{x}{i}} \mu (p) \\ &= \sum_{p |x} \sum_{i | p} a_i \mu(\frac{p}{i}) \\ &= \sum_{p |x} (a * \mu)(p) \end{aligned} \]

對於數組 \(a\),不妨設 \(f(a) = a * \mu\),那么原式等於:

\[\begin{aligned}\sum_{k = 1}^n C_k \sum_{i = 1}^n \sum_{j = 1}^n A_iB_j D_{(i, j)} \sum_{p | (i, k)} f(E)(p)\sum_{q | (j, k)}\end{aligned} f(F)(q) \]

\(g(a)(x) = \sum_\limits{x | i}a_i\),原式等於:

\[\begin{aligned}&\sum_{{\rm lcm}(p, q) \leq n} f(E)(p) \times f(F)(q) \times g(C)({\rm lcm}(p, q)) \sum_{ip \leq n}A_{ip} \sum_{jq \leq n}B_{jq} D_{(ip, jq)} \\ =& \sum_{d = 1}^n \sum_{xy \leq \left\lfloor\frac{n}{d}\right\rfloor, (x, y) = 1} f(E)(xd) \times f(F)(yd) \times g(C)(xyd)\sum_{ix \leq \left\lfloor\frac{n}{d}\right\rfloor} \sum_{jy \leq \left\lfloor\frac{n}{d}\right\rfloor} A_{ixd} B_{jyd} D_{d \times (ix, jy)}\end{aligned} \]

\(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\),原式等於:

\[\begin{aligned} \sum_{d = 1}^n \sum_{xy \leq m, (x, y) = 1} P_x Q_y R_{xy} \sum_{ix \leq m} \sum_{jy \leq m} S_{ix} T_{jy} W_{(ix, jy)} \end{aligned} \]

對於每個 \(d\),我們可以先預處理出數組 \(P, Q, R, S, T, W\)。這樣,如果 \(x\)\(y\) 也已知,那么 \(P_xQ_yR_{xy}\) 可以直接得到。考慮如何求后面的部分:

\[\begin{aligned} &\sum_{ix \leq m} \sum_{jy \leq m} S_{ix} T_{jy} W_{(ix, jy)} \\ = & \sum_{ix \leq m} S_{ix} \sum_{jy \leq m} T_{jy} \sum_{r | (ix, jy)} f(W)(r) \\ = & \sum_{jy \leq m} T_{jy} \sum_{r | jy} f(W)(r) \sum_{r | ix} S_{ix}\end{aligned} \]

\(u = ix, v= jy\),那么上式等於:

\[\begin{aligned}\sum_{y | v} T_{v} \sum_{r | v} f(W)(r) \sum_{ r | u} [x | u]S_{u}\end{aligned} \]

\(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]\) 代入原式得:

\[\begin{aligned} \sum_{i = 1}^A \sum_{j = 1}^B \sum_{k = 1}^C d(ijk) &= \sum_{i = 1}^A \sum_{j = 1}^B \sum_{k = 1}^C \sum_{x | i} \sum_{y | j}\sum_{z|k} [(x, y) = 1][(y, z) = 1][(x, z) = 1] \\ &= \sum_{x = 1}^A \sum_{y = 1}^B \sum_{z = 1}^C [(x, y) = 1][(y, z) = 1][(x, z) = 1]\left\lfloor\frac{A}{x}\right\rfloor \left\lfloor\frac{B}{y}\right\rfloor \left\lfloor\frac{C}{z}\right\rfloor\end{aligned} \]

定義函數 \(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]\)。那么原式即為:

\[\sum_{x = 1}^A \sum_{y = 1}^B \sum_{z = 1}^C a_x b_y c_z f_{(x, y)}f_{(y, z)}f_{(x, z)} \]

至此,我們得到了和上題形式完全一樣的式子。因此直接套用上題做法即可。單組數據的時間復雜度不變。不過有點卡常,因為本題的正解壓根就不是這個。

代碼

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM