[bzoj3994][SDOI2015]约数个数和-数论

Brief Description

计算\(\sum_{i\leqslant n}\sum_{j\leqslant m}\sigma_0(ij)\).

Algorithm Design

首先证明一个结论

\[d(ij) = \sum_{i\leqslant n}\sum_{j \leqslant m}[(i,j)=1] \]

我们不显式地证明它, 仅仅直观地考虑每个质数对于答案的贡献就好.
有了这个结论我们开始推式子:

\[\begin{aligned} \sum_{i\leqslant n}\sum_{j\leqslant m}\sigma_0(ij) & = \sum_{i\leqslant n}\sum_{j\leqslant m} \sum_{a|i} \sum_{b|j} [(i,j)=1]\\ & =\sum_{i\leqslant n}\sum_{j\leqslant m}\sum_{a|i}\sum_{b|j}\sum_{x|(i,j)}\mu(x)\\& =\sum_{i\leqslant n}\sum_{j \leqslant m}\sum_{x|i+j}\mu(x)\sigma_0(\frac ix)\sigma_0(\frac jx) \\ &=\sum_{x \leqslant n} \mu(x)\sum_{i \leqslant \lfloor \frac nx \rfloor} \sigma_0(i)\sum_{j \leqslant \lfloor \frac mx \rfloor}\sigma_0(j) \end{aligned} \]

有了最后的式子, 我们就可以开始乱搞了.
复杂度\(O(n+T\sqrt n)\)

Code

#include <algorithm>
#include <cctype>
#include <cstdio>
#define ll long long
const int maxn = 50000 + 10;
int prime[maxn], tot;
int mu[maxn], sigma[maxn], summu[maxn];
ll sumsigma[maxn];
bool check[maxn];
int read() {
  int x = 0, f = 1;
  char ch = getchar();
  while (!isdigit(ch)) {
    if (ch == '-')
      f = -1;
    ch = getchar();
  }
  while (isdigit(ch)) {
    x = x * 10 + ch - '0';
    ch = getchar();
  }
  return x * f;
}
void shake() {
  int minPrimeCnt[maxn];
  mu[1] = 1, sigma[1] = 1;
  for (int i = 2; i < maxn; i++) {
    if (!check[i]) {
      prime[tot++] = i;
      mu[i] = -1;
      sigma[i] = 2;
      minPrimeCnt[i] = 1;
    }
    for (int j = 0; j < tot; j++) {
      int x = i * prime[j];
      if (x >= maxn)
        break;
      check[x] = 1;
      if (i % prime[j] == 0) {
        mu[x] = 0;
        minPrimeCnt[x] = minPrimeCnt[i] + 1;
        sigma[x] = sigma[i] / (minPrimeCnt[i] + 1) * (minPrimeCnt[x] + 1);
        break;
      } else {
        mu[x] = -mu[i];
        sigma[x] = sigma[i] << 1;
        minPrimeCnt[x] = 1;
      }
    }
  }
  summu[0] = 0;
  for (int i = 1; i < maxn; i++)
    summu[i] = summu[i - 1] + mu[i];
  for (int i = 1; i < maxn; i++)
    sumsigma[i] = sumsigma[i - 1] + sigma[i];
}
ll F(int n, int m) {
  if (n > m)
    std::swap(n, m);
  ll ans = 0;
  for (int i = 1, last = 1; i <= n; i = last + 1) {
    last = std::min(n / (n / i), m / (m / i));
    ans += (summu[last] - summu[i - 1]) * sumsigma[n / i] * sumsigma[m / i];
  }
  return ans;
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("input", "r", stdin);
#endif
  shake();
  int kase = read();
  while (kase--) {
    int n = read(), m = read();
    printf("%lld\n", F(n, m));
  }
  return 0;
}

posted on 2017-03-21 16:06  蒟蒻konjac  阅读(215)  评论(0编辑  收藏  举报