【每日一题】Problem 414B. Mashmokh and ACM

原题

解决思路

  1. 先计算 \([1, n]\) 中的约数集合
  2. \(dp[i][j](i\in [1, n], j\in [1, k])\) 表示第 \(j\) 个位置放置 \(i\) 所拥有的可能性,即 \(i\) 的约数集合的可能性总和
  3. 以此类推,到达 \(k\) 时,计算 \(\sum_{i=1}^{n}dp[i][k]\) 即可
#include <bits/stdc++.h>

int main() {
  int n, k; std::cin >> n >> k;
  std::map<int, std::vector<int>> m;
  m[0] = std::vector<int>(n + 1, 0);
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= i; ++j) {
      if (i % j == 0) {
        if (m.find(i) == m.end()) m[i] = std::vector<int>();
        m[i].push_back(j);
      }
    }
    m[0][i] = i;
  }
  long long ans = 0;
  std::vector<std::vector<long long>> dp(
      n + 1, std::vector<long long>(k + 1, 0));
  // 由于约数不可能超过自己,因此以按序计算出 i 在 j 位置的可能性的顺序是可以的
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= k; ++j) {
      if (j == 1) dp[i][j] = 1;
      else {
	// 遍历约数集合
        for (auto &v : m[i]) {
          dp[i][j] += dp[v][j - 1];
          dp[i][j] %= 1000000007;
        }
      }
      if (j == k) {
        ans += dp[i][j];
        ans %= 1000000007;
      }
    }
  }

  std::cout << ans << std::endl;
  return 0;
}

更好的解

上面的方法中,由于使用 \(O(n^2)\) 的复杂度计算约数,以及频繁地 push_back,导致比较耗时
看题解时看到优化后的思路

// 遍历可以整除 j 的 m
      for (int m = j; m <= n; m += j) {
        dp[i][m] = (dp[i][m] + dp[i - 1][j]) % MODULO;
      }

更新后的代码如下

#include <bits/stdc++.h>
#define MODULO 1000000007;

int main() {
  int n, k; std::cin >> n >> k;
  long long ans = 0;
  std::vector<std::vector<int>> dp(
      k + 1, std::vector<int>(n + 1, 0));

  for (int i = 1; i <= n; ++i) dp[1][i] = 1;
  for (int i = 1; i <= k; ++i) {
    for (int j = 1; j <= n; ++j) {
      // 遍历可以整除 j 的 m
      for (int m = j; m <= n; m += j) {
        dp[i][m] = (dp[i][m] + dp[i - 1][j]) % MODULO;
      }
      if (i == k) {
        ans += dp[i][j];
        ans %= MODULO;
      }
    }
  }
  std::cout << ans << std::endl;
  return 0;
}
cache 命中

方法二中替换了方法一的 \(i, j\) 的顺序,但是将方法二修改为方法一的方式仍然是可以的,因为约数不会超过自己
但是需要注意的是,如果修改为方法一的顺序,会影响 cache 命中,因为需要频繁地修改而不是

posted @ 2023-07-02 21:30  HelloEricy  阅读(11)  评论(0编辑  收藏  举报