[HAOI2011]Problem b - 莫比乌斯反演

Description

对于给出的 \(n\) 个询问,每次求有多少个数对 \((x,y)\),满足 \(a \le x \le b\)\(c \le y \le d\),且 \(\gcd(x,y) = k\)\(\gcd(x,y)\) 函数为 \(x\)\(y\) 的最大公约数。

其中 \(1 \le n,k \le 5 \times 10^4\)\(1 \le a \le b \le 5 \times 10^4\)\(1 \le c \le d \le 5 \times 10^4\)

Solution

首先肯定是自然而然地想到分成四块容斥,即

\[\begin{split} \sum \limits_{i=a}^{b} \sum \limits_{j=c}^{d} \left[ gcd(i,j) = k \right] &= \sum \limits_{i=1}^{b} \sum \limits_{j=1}^{d} \left[ gcd(i,j) =k \right] \\ &- \sum \limits_{i=1}^{a} \sum \limits_{j=1}^{d} \left[ gcd(i,j) =k \right]\\ &- \sum \limits_{i=1}^{b} \sum \limits_{j=1}^{c} \left[ gcd(i,j) =k \right]\\ &+ \sum \limits_{i=1}^{a} \sum \limits_{j=1}^{c} \left[ gcd(i,j) =k \right] \end{split} \]

这样,每一部分都是 \(\sum \limits_{i=1}^{m} \sum \limits_{j=1}^{m} \left[ gcd(i,j) = k \right]\) 的形式。

接下来考虑如何将式子进行变换:

\[\sum \limits_{i=1}^{m} \sum \limits_{j=1}^{m} \left[ gcd(i,j) = k \right] = \sum \limits_{i=1}^{\lfloor \frac{n}{k} \rfloor} \sum \limits_{j=1}^{\lfloor \frac{m}{k} \rfloor} \left[ gcd(i,j) = 1 \right] \]

看到右边的 \(\left[ gcd(i,j) = 1 \right]\) 是不是感觉很熟悉?根据在莫比乌斯反演学习笔记中提到的反演结论

\[\left[ gcd(i,j) = 1 \right] \iff \sum \limits_{d \mid gcd(i,j)} \mu(d) \]

可以继续化简:

\[\sum \limits_{i=1}^{\lfloor \frac{n}{k} \rfloor} \sum \limits_{j=1}^{\lfloor \frac{m}{k} \rfloor} \sum \limits_{d \mid gcd(i,j)} \mu(d) \]

考虑变换求和顺序:

\[\sum \limits_{d=1}^{\min(n,m)} \mu(d) \sum \limits_{i=1}^{\lfloor \frac{n}{k} \rfloor} \left[ d \mid i \right] \sum \limits_{j=1}^{\lfloor \frac{m}{k} \rfloor} \left[ d \mid j \right] \]

易知在 \(\left[ 1, n\right]\) 中约数包含 \(d\) 的数有 \(\lfloor \frac{n}{d} \rfloor\) 个,于是式子可以化简为:

\[\sum \limits_{d=1}^{\min(n,m)} \mu(d) \lfloor \frac{\lfloor \frac{n}{k} \rfloor}{d} \rfloor \lfloor \frac{\lfloor \frac{m}{k} \rfloor}{d} \rfloor \]

再由莫比乌斯反演学习笔记中提到的引理1,可得:

\[\sum \limits_{d=1}^{\min(n,m)} \mu(d) \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor \]

所以最终,就得到了

\[\sum \limits_{i=a}^{b} \sum \limits_{j=c}^{d} \left[ gcd(i,j) = k \right] = \sum \limits_{d=1}^{\min(n,m)} \mu(d) \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor \]

这个式子就可以利用数论分块进行求解了。

Code

Talk is cheap. Show me the code.

#include <bits/stdc++.h>
using namespace std;

int ty() {
  int x = 0, f = 1; char ch = getchar();
  while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
  while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
  return x * f;
}

const int _ = 5e4 + 10;
int tot, vis[_], p[_], mu[_], sum[_];

void getMu(int lim = 5e4) {
  mu[1] = 1;
  for (int i = 2; i <= lim; ++i) {
    if (!vis[i]) p[++tot] = i, mu[i] = -1;
    for (int j = 1; j <= tot && p[j] * i <= lim; ++j) {
      vis[i * p[j]] = 1;
      if (i % p[j] == 0) {
        mu[i * p[j]] = 0;
        break;
      }
      mu[i * p[j]] = -mu[i];
    }
  }
  for (int i = 1; i <= lim; ++i) sum[i] = sum[i - 1] + mu[i];
}

int calc(int n, int m, int k) {
  n /= k, m /= k;
  int ret = 0;
  for (int l = 1, r; l <= min(n, m); l = r + 1) {
    r = min(n / (n / l), m / (m / l));
    ret += (sum[r] - sum[l - 1]) * (n / l) * (m / l);
  }
  return ret;
}

int main() {
  getMu();
  int T = ty(), a, b, c, d, k;
  while (T--) {
    a = ty(), b = ty(), c = ty(), d = ty(), k = ty();
    printf("%d\n", calc(b, d, k) - calc(a - 1, d, k) - calc(b, c - 1, k) +
                       calc(a - 1, c - 1, k));
  }
  return 0;
}
posted @ 2020-06-14 19:01  newbielyx  阅读(107)  评论(0编辑  收藏  举报