zoukankan      html  css  js  c++  java
  • 51nod1238. 最小公倍数之和 V3(数论)

    题目链接

    https://www.51nod.com/Challenge/Problem.html#!#problemId=1238

    题解

    本来想做个杜教筛板子题结果用另一种方法过了......

    所谓的“另一种方法”用到的一些技巧还是挺不错的,因此这里简单介绍一下。

    首先还是基本的推式子:

    [egin{aligned}sum_{i = 1}^n sum_{j = 1}^n { m lcm}(i, j) &= sum_{i = 1}^n sum_{j = 1}^n frac{ij}{{ m gcd}(i, j)} \ &= sum_{d = 1}^{n} sum_{i = 1}^n sum_{j = 1}^n[{ m gcd}(i, j) = d]frac{ij}{d} \ &= sum_{d = 1}^n dsum_{i = 1}^{leftlfloorfrac{n}{d} ight floor}sum_{j = 1}^{leftlfloorfrac{n}{d} ight floor}[{ m gcd}(i, j) = 1]ijend{aligned} ]

    (f(x) = sum_limits{i = 1}^x sum_limits{j = 1}^x [{ m gcd}(i, j) = 1]ij),那么答案即为 (sum_limits{d = 1}^n d imes f(leftlfloorfrac{n}{d} ight floor))。显然答案可以数论分块求,因此我们的任务就是求 (f) 函数。

    首先考虑当 (x) 较小时,我们能否直接预处理出 (f(x))。考虑差分:当 (x > 1) 时,(f(x))(f(x - 1)) 而言,多了的部分为:$$left(sum_limits{i = 1}^xsum_limits{j = 1}^x [{ m gcd}(i, j) = 1]ij ight) - left(sum_limits{i = 1}^{x - 1}sum_limits{j = 1}^{x - 1} [{ m gcd}(i, j) = 1]ij ight) = 2x sum_{i = 1}^x [{ m gcd}(i, x) = 1]i$$

    而由于小于等于 (x(x > 1)) 且与 (x) 互质的数的和为 (frac{varphi(x)x}{2})(证明提示:当 (n geq 2) 时,若 ({ m gcd}(d, n) = 1) 必然有 ({ m gcd}(n - d, n) = 1),与 (n) 互质的 (d) 共有 (varphi(n)) 个),因此我们就得到了 (f(x)) 的递推式:(f(x) = f(x - 1) + 2x imes frac{varphi(x)x}{2}),即 (f(x) = f(x - 1) + varphi(x)x^2)

    不过这只能处理 (x) 较小的情况。当 (x) 较大时,我们仍然得另谋他路。在 (f(x) = sum_limits{i = 1}^x sum_limits{j = 1}^x [{ m gcd}(i, j) = 1]ij) 当中,对 (f(x)) 有贡献的 (i, j) 满足 (i)(j) 是互质的,我们尝试着使用补集转化,用总和减去不互质的 (i, j) 的贡献:

    [egin{aligned} f(x) &= sum_{i = 1}^xsum_{j = 1}^x ij - sum_{d = 2}^x sum_{i = 1}^x sum_{j = 1}^x [{ m gcd}(i, j) = d]ij \ &= left(frac{x(x+1)}{2} ight)^2 - sum_{d = 2}^x d^2 sum_{i = 1}^{leftlfloorfrac{x}{d} ight floor} sum_{j = 1}^{leftlfloorfrac{x}{d} ight floor} [{ m gcd}(i, j) = 1]ij \ &=left(frac{x(x+1)}{2} ight)^2 - sum_{d = 2}^x d^2 f(leftlfloorfrac{x}{d} ight floor) end{aligned} ]

    这样,我们就能够递归地去求解当 (x) 较大时 (f(x)) 的值了。不难发现,该求解方法的时间复杂度和杜教筛是一样的,为 (O(n^{frac{2}{3}})),且非常好写。

    代码

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int mod = 1000000007, inv2 = 500000004, inv6 = 166666668, up = 4641589, md = 1000003;
    
    int main() {
      function<int (int, int)> mul = [&] (int x, int y) {
        return (long long) x * y % mod;
      };
      function<void (int&, int)> add = [&] (int& x, int y) {
        x += y;
        if (x >= mod) {
          x -= mod;
        }
      };
      function<void (int&, int)> sub = [&] (int& x, int y) {
        x -= y;
        if (x < 0) {
          x += mod;
        }
      };
      vector<bool> is_prime(up, true);
      vector<int> phi(up), primes;
      phi[1] = 1;
      for (int i = 2; i < up; ++i) {
        if (is_prime[i]) {
          primes.push_back(i);
          phi[i] = i - 1;
        }
        for (auto v : primes) {
          int d = v * i;
          if (d >= up) {
            break;
          }
          is_prime[d] = false;
          if (i % v == 0) {
            phi[d] = mul(phi[i], v);
            break;
          } else {
            phi[d] = mul(phi[i], phi[v]);
          }
        }
      }
      for (int i = 2; i < up; ++i) {
        phi[i] = mul(mul(phi[i], i), i);
        add(phi[i], phi[i - 1]);
      }
      function<int (long long)> sum_pow2 = [&] (long long n) {
        n %= mod;
        return mul(mul(mul(n, n + 1), (n * 2 + 1)), inv6);
      };
      vector<pair<long long, int>> value[md];
      function<int (long long)> find = [&] (long long v) {
        int p = v % md;
        for (auto e : value[p]) {
          if (e.first == v) {
            return e.second;
          }
        }
        return -1;
      };
      function<void (long long, int)> insert = [&] (long long v, int result) {
        value[v % md].emplace_back(v, result);
      };
      function<int (long long)> f = [&] (long long n) {
        int returned = find(n);
        if (~returned) {
          return returned;
        } else {
          if (n < up) {
            return phi[n];
          } else {
            int result = 0, x = n % mod;
            x = mul(mul(x, x + 1), inv2);
            result = mul(x, x);
            for (long long i = 2, last; i <= n; i = last + 1) {
              last = n / (n / i);
              sub(result, mul((sum_pow2(last) - sum_pow2(i - 1) + mod) % mod, f(n / i)));
            }
            insert(n, result);
            return result;
          }
        }
      };
      long long n;
      scanf("%lld", &n);
      int answer = 0;
      for (long long i = 1, last; i <= n; i = last + 1) {
        last = n / (n / i);
        add(answer, mul(mul(mul((i + last) % mod, (last - i + 1) % mod), inv2), f(n / i)));
      }
      printf("%d
    ", answer);
      return 0;
    }
    
  • 相关阅读:
    华硕笔记本无法U盘启动,快捷键识别不了
    怎么将uefi改成legacy启动|BIOS设置legacy引导模式的方法
    [CDLinux]制作U盘CDLinux系统启动盘
    重装系统时,将MBR分区转为GPT 分区
    5-Comments
    4-HTML Computer Code Elements
    3-html 缩写-地址-文字方向-引用块-题注的格式
    2-HTML Text Formatting Elements
    1-HTML Attributes
    LabVIEW--为设备添加配置文件.ini
  • 原文地址:https://www.cnblogs.com/ImagineC/p/10121395.html
Copyright © 2011-2022 走看看