跳转至

# Powerful Number 筛

## 定义

Powerful Number（以下简称 PN）筛类似于杜教筛，或者说是杜教筛的一个扩展，可以拿来求一些积性函数的前缀和．

要求：

- 存在一个函数 𝑔 g 满足：
  - 𝑔 g 是积性函数．
  - 𝑔 g 易求前缀和．
  - 对于质数 𝑝 p，𝑔(𝑝) =𝑓(𝑝)g(p)=f(p)．

假设现在要求积性函数 𝑓 f 的前缀和 𝐹(𝑛) =∑𝑛 𝑖=1 𝑓(𝑖)F(n)=∑i=1 n f(i)．

## Powerful Number

定义：对于正整数 𝑛 n，记 𝑛 n 的质因数分解为 𝑛 =∏𝑚 𝑖=1 𝑝 𝑒 𝑖 𝑖 n=∏i=1 m p i e i．𝑛 n 是 PN 当且仅当 ∀1 ≤𝑖 ≤𝑚,𝑒 𝑖 >1∀1≤i≤m,e i>1．

性质 1：所有 PN 都可以表示成 𝑎 2 𝑏 3 a 2 b 3 的形式．

证明：若 𝑒 𝑖 e i 是偶数，则将 𝑝 𝑒 𝑖 𝑖 p i e i 合并进 𝑎 2 a 2 里；若 𝑒 𝑖 e i 为奇数，则先将 𝑝 3 𝑖 p i 3 合并进 𝑏 3 b 3 里，再将 𝑝 𝑒 𝑖−3 𝑖 p i e i−3 合并进 𝑎 2 a 2 里．

性质 2：𝑛 n 以内的 PN 至多有 𝑂(√𝑛)O(n) 个．

证明：考虑枚举 𝑎 a，再考虑满足条件的 𝑏 b 的个数，有 PN 的个数约等于

∫√𝑛 1 3√𝑛 𝑥 2 d 𝑥=𝑂(√𝑛)∫1 n n x 2 3 d x=O(n)

那么如何求出 𝑛 n 以内所有的 PN 呢？线性筛找出 √𝑛 n 内的所有素数，再 DFS 搜索各素数的指数即可．由于 𝑛 n 以内的 PN 至多有 𝑂(√𝑛)O(n) 个，所以至多搜索 𝑂(√𝑛)O(n) 次．

## PN 筛

首先，构造出一个易求前缀和的积性函数 𝑔 g，且满足对于素数 𝑝 p，𝑔(𝑝) =𝑓(𝑝)g(p)=f(p)．记 𝐺(𝑛) =∑𝑛 𝑖=1 𝑔(𝑖)G(n)=∑i=1 n g(i)．

然后，构造函数 ℎ =𝑓/𝑔 h=f/g，这里的 // 表示狄利克雷卷积除法．根据狄利克雷卷积的性质可以得知 ℎ h 也为积性函数，因此 ℎ(1) =1 h(1)=1．𝑓 =𝑔 ∗ℎ f=g∗h，这里 ∗∗ 表示狄利克雷卷积．

对于素数 𝑝 p，𝑓(𝑝) =𝑔(1)ℎ(𝑝) +𝑔(𝑝)ℎ(1) =ℎ(𝑝) +𝑔(𝑝) ⟹ ℎ(𝑝) =0 f(p)=g(1)h(p)+g(p)h(1)=h(p)+g(p)⟹h(p)=0．根据 ℎ(𝑝) =0 h(p)=0 和 ℎ h 是积性函数可以推出对于非 PN 的数 𝑛 n 有 ℎ(𝑛) =0 h(n)=0，即 ℎ h 仅在 PN 处取有效值．

现在，根据 𝑓 =𝑔 ∗ℎ f=g∗h 有

𝐹(𝑛)=𝑛∑𝑖=1 𝑓(𝑖)=𝑛∑𝑖=1∑𝑑|𝑖 ℎ(𝑑)𝑔(𝑖 𝑑)=𝑛∑𝑑=1⌊𝑛 𝑑⌋∑𝑖=1 ℎ(𝑑)𝑔(𝑖)=𝑛∑𝑑=1 ℎ(𝑑)⌊𝑛 𝑑⌋∑𝑖=1 𝑔(𝑖)=𝑛∑𝑑=1 ℎ(𝑑)𝐺(⌊𝑛 𝑑⌋)=𝑛∑𝑑=1 𝑑 i s P N ℎ(𝑑)𝐺(⌊𝑛 𝑑⌋)F(n)=∑i=1 n f(i)=∑i=1 n∑d|i h(d)g(i d)=∑d=1 n∑i=1⌊n d⌋h(d)g(i)=∑d=1 n h(d)∑i=1⌊n d⌋g(i)=∑d=1 n h(d)G(⌊n d⌋)=∑d=1 d is PN n h(d)G(⌊n d⌋)

𝑂(√𝑛)O(n) 找出所有 PN，计算出所有 ℎ h 的有效值．对于 ℎ h 有效值的计算，只需要计算出所有 ℎ(𝑝 𝑐)h(p c) 处的值，就可以根据 ℎ h 为积性函数推出 ℎ h 的所有有效值．现在对于每一个有效值 𝑑 d，计算 ℎ(𝑑)𝐺(⌊𝑛 𝑑⌋)h(d)G(⌊n d⌋) 并累加即可得到 𝐹(𝑛)F(n)．

下面考虑计算 ℎ(𝑝 𝑐)h(p c)，一共有两种方法：一种是直接推出 ℎ(𝑝 𝑐)h(p c) 仅与 𝑝,𝑐 p,c 有关的计算公式，再根据公式计算 ℎ(𝑝 𝑐)h(p c)；另一种是根据 𝑓 =𝑔 ∗ℎ f=g∗h 有 𝑓(𝑝 𝑐) =∑𝑐 𝑖=0 𝑔(𝑝 𝑖)ℎ(𝑝 𝑐−𝑖)f(p c)=∑i=0 c g(p i)h(p c−i)，移项可得 ℎ(𝑝 𝑐) =𝑓(𝑝 𝑐) −∑𝑐 𝑖=1 𝑔(𝑝 𝑖)ℎ(𝑝 𝑐−𝑖)h(p c)=f(p c)−∑i=1 c g(p i)h(p c−i)，现在就可以枚举素数 𝑝 p 再枚举指数 𝑐 c 求解出所有 ℎ(𝑝 𝑐)h(p c)．

### 过程

1. 构造 𝑔 g
2. 构造快速计算 𝐺 G 的方法
3. 计算 ℎ(𝑝 𝑐)h(p c)
4. 搜索 PN，过程中累加答案
5. 得到结果

对于第 3 步，可以直接根据公式计算，可以使用枚举法预处理打表，也可以搜索到了再临时推．

### 性质

以使用第二种方法计算 ℎ(𝑝 𝑐)h(p c) 为例进行分析．可以分为计算 ℎ(𝑝 𝑐)h(p c) 和搜索两部分进行分析．

对于第一部分，根据 𝑂(√𝑛)O(n) 内的素数个数为 𝑂(√𝑛 l o g⁡𝑛)O(n log⁡n)，每个素数 𝑝 p 的指数 𝑐 c 至多为 l o g⁡𝑛 log⁡n，计算 ℎ(𝑝 𝑐)h(p c) 需要循环 (𝑐 −1)(c−1) 次，由此有第一部分的时间复杂度为 𝑂(√𝑛 l o g⁡𝑛⋅l o g⁡𝑛⋅l o g⁡𝑛) =𝑂(√𝑛 l o g⁡𝑛)O(n log⁡n⋅log⁡n⋅log⁡n)=O(n log⁡n)，且这是一个宽松的上界．根据题目的不同还可以添加不同的优化，从而降低第一部分的时间复杂度．

对于搜索部分，由于 𝑛 n 以内的 PN 至多有 𝑂(√𝑛)O(n) 个，所以至多搜索 𝑂(√𝑛)O(n) 次．对于每一个 PN，根据计算 𝐺 G 的方法不同，时间复杂度也不同．例如，假设计算 𝐺(⌊𝑛 𝑑⌋)G(⌊n d⌋) 的时间复杂度为 𝑂(1)O(1)，则第二部分的复杂度为 𝑂(√𝑛)O(n)．

特别地，若借助杜教筛计算 𝐺(⌊𝑛 𝑑⌋)G(⌊n d⌋)，则第二部分的时间复杂度为杜教筛的时间复杂度，即 𝑂(𝑛 2 3)O(n 2 3)．因为若事先计算一次 𝐺(𝑛)G(n)，并且预先使用线性筛优化和用支持快速随机访问的数据结构（如 C++ 中的 `std::map` 和 `std::unordered_map`）记录较大的值，则杜教筛过程中用到的 𝐺(⌊𝑛 𝑑⌋)G(⌊n d⌋) 都是线性筛中记录的或者 `std::map` 中记录的，这一点可以直接用程序验证．

对于空间复杂度，其瓶颈在于存储 ℎ(𝑝 𝑐)h(p c)．若使用二维数组 𝑎 a 记录，𝑎 𝑖,𝑗 a i,j 表示 ℎ(𝑝 𝑗 𝑖)h(p i j) 的值，则空间复杂度为 𝑂(√𝑛 l o g⁡𝑛⋅l o g⁡𝑛) =𝑂(√𝑛)O(n log⁡n⋅log⁡n)=O(n)．

## 例题

### Luogu P5325【模板】Min_25 筛

题意：给定积性函数 𝑓(𝑝 𝑘) =𝑝 𝑘(𝑝 𝑘 −1)f(p k)=p k(p k−1)，求 ∑𝑛 𝑖=1 𝑓(𝑖)∑i=1 n f(i)．

易得 𝑓(𝑝) =𝑝(𝑝 −1) =i d⁡(𝑝)𝜑(𝑝)f(p)=p(p−1)=id⁡(p)φ(p)，构造 𝑔(𝑛) =i d⁡(𝑛)𝜑(𝑛)g(n)=id⁡(n)φ(n)．

考虑使用杜教筛求 𝐺(𝑛)G(n)，根据 (i d ⋅𝜑) ∗i d =i d 2(id⋅φ)∗id=id 2 可得 𝐺(𝑛) =∑𝑛 𝑖=1 𝑖 2 −∑𝑛 𝑑=2 𝑑 ⋅𝐺(⌊𝑛 𝑑⌋)G(n)=∑i=1 n i 2−∑d=2 n d⋅G(⌊n d⌋)．

之后 ℎ(𝑝 𝑘)h(p k) 的取值可以枚举计算，这种方法不再赘述．

此外，此题还可以直接求出 ℎ(𝑝 𝑘)h(p k) 仅与 𝑝,𝑘 p,k 有关的公式，过程如下：

𝑓(𝑝 𝑘)=𝑘∑𝑖=0 𝑔(𝑝 𝑘−𝑖)ℎ(𝑝 𝑖)⟺𝑝 𝑘(𝑝 𝑘−1)=𝑘∑𝑖=0 𝑝 𝑘−𝑖 𝜑(𝑝 𝑘−𝑖)ℎ(𝑝 𝑖)⟺𝑝 𝑘(𝑝 𝑘−1)=𝑘∑𝑖=0 𝑝 2 𝑘−2 𝑖−1(𝑝−1)ℎ(𝑝 𝑖)⟺𝑝 𝑘(𝑝 𝑘−1)=ℎ(𝑝 𝑘)+𝑘−1∑𝑖=0 𝑝 2 𝑘−2 𝑖−1(𝑝−1)ℎ(𝑝 𝑖)⟺ℎ(𝑝 𝑘)=𝑝 𝑘(𝑝 𝑘−1)−𝑘−1∑𝑖=0 𝑝 2 𝑘−2 𝑖−1(𝑝−1)ℎ(𝑝 𝑖)⟺ℎ(𝑝 𝑘)−𝑝 2 ℎ(𝑝 𝑘−1)=𝑝 𝑘(𝑝 𝑘−1)−𝑝 𝑘+1(𝑝 𝑘−1−1)−𝑝(𝑝−1)ℎ(𝑝 𝑘−1)⟺ℎ(𝑝 𝑘)−𝑝 ℎ(𝑝 𝑘−1)=𝑝 𝑘+1−𝑝 𝑘⟺ℎ(𝑝 𝑘)𝑝 𝑘−ℎ(𝑝 𝑘−1)𝑝 𝑘−1=𝑝−1 f(p k)=∑i=0 k g(p k−i)h(p i)⟺p k(p k−1)=∑i=0 k p k−i φ(p k−i)h(p i)⟺p k(p k−1)=∑i=0 k p 2 k−2 i−1(p−1)h(p i)⟺p k(p k−1)=h(p k)+∑i=0 k−1 p 2 k−2 i−1(p−1)h(p i)⟺h(p k)=p k(p k−1)−∑i=0 k−1 p 2 k−2 i−1(p−1)h(p i)⟺h(p k)−p 2 h(p k−1)=p k(p k−1)−p k+1(p k−1−1)−p(p−1)h(p k−1)⟺h(p k)−p h(p k−1)=p k+1−p k⟺h(p k)p k−h(p k−1)p k−1=p−1

再根据 ℎ(𝑝) =0 h(p)=0，通过累加法即可推出 ℎ(𝑝 𝑘) =(𝑘 −1)(𝑝 −1)𝑝 𝑘 h(p k)=(k−1)(p−1)p k．

参考代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    #include <iostream>
    #include <map>
    using namespace std;

    constexpr int MOD = 1e9 + 7;

    template <typename T>
    int mint(T x) {
      x %= MOD;
      if (x < 0) x += MOD;
      return x;
    }

    int add(int x, int y) { return x + y >= MOD ? x + y - MOD : x + y; }

    int mul(int x, int y) { return (long long)1 * x * y % MOD; }

    int sub(int x, int y) {
      return x < y ? x - y + MOD : x - y;  // 防止负数
    }

    int qp(int x, int y) {
      int r = 1;
      for (; y; y >>= 1) {
        if (y & 1) r = mul(r, x);
        x = mul(x, x);
      }
      return r;
    }

    int inv(int x) { return qp(x, MOD - 2); }

    namespace PNS {
    constexpr int N = 2e6 + 5;
    constexpr int M = 35;

    long long global_n;

    int g[N], sg[N];

    int h[N][M];
    bool vis_h[N][M];

    int ans;

    int pcnt, prime[N], phi[N];
    bool isp[N];

    void sieve(int n) {
      pcnt = 0;
      for (int i = 2; i <= n; ++i) isp[i] = true;  // 判断质数数组
      phi[1] = 1;
      for (int i = 2; i <= n; ++i) {
        if (isp[i]) {
          ++pcnt;
          prime[pcnt] = i;
          phi[i] = i - 1;
        }
        for (int j = 1; j <= pcnt; ++j) {  // 筛去非质数
          long long nxt = (long long)1 * i * prime[j];
          if (nxt > n) break;
          isp[nxt] = false;
          if (i % prime[j] == 0) {  // i是非质数的情况
            phi[nxt] = phi[i] * prime[j];
            break;
          }
          phi[nxt] = phi[i] * phi[prime[j]];
        }
      }

      for (int i = 1; i <= n; ++i) g[i] = mul(i, phi[i]);

      sg[0] = 0;
      for (int i = 1; i <= n; ++i) sg[i] = add(sg[i - 1], g[i]);  // g函数的前缀和
    }

    int inv2, inv6;

    void init() {
      sieve(N - 1);
      for (int i = 1; i <= pcnt; ++i) h[i][0] = 1, h[i][1] = 0;
      for (int i = 1; i <= pcnt; ++i) vis_h[i][0] = vis_h[i][1] = true;
      inv2 = inv(2);
      inv6 = inv(6);
    }

    int S1(long long n) { return mul(mul(mint(n), mint(n + 1)), inv2); }

    int S2(long long n) {
      return mul(mul(mint(n), mul(mint(n + 1), mint(n * 2 + 1))), inv6);
    }

    map<long long, int> mp_g;

    int G(long long n) {
      if (n < N) return sg[n];
      if (mp_g.count(n)) return mp_g[n];

      int ret = S2(n);
      for (long long i = 2, j; i <= n; i = j + 1) {
        j = n / (n / i);
        ret = sub(ret, mul(sub(S1(j), S1(i - 1)), G(n / i)));
      }
      mp_g[n] = ret;
      return ret;
    }

    void dfs(long long d, int hd, int pid) {
      ans = add(ans, mul(hd, G(global_n / d)));

      for (int i = pid; i <= pcnt; ++i) {
        if (i > 1 && d > global_n / prime[i] / prime[i]) break;  // 剪枝

        int c = 2;
        for (long long x = d * prime[i] * prime[i]; x <= global_n;
             x *= prime[i], ++c) {  // 计算f.g函数
          if (!vis_h[i][c]) {
            int f = qp(prime[i], c);
            f = mul(f, sub(f, 1));
            int g = mul(prime[i], prime[i] - 1);
            int t = mul(prime[i], prime[i]);

            for (int j = 1; j <= c; ++j) {
              f = sub(f, mul(g, h[i][c - j]));
              g = mul(g, t);
            }
            h[i][c] = f;
            vis_h[i][c] = true;
          }

          if (h[i][c]) dfs(x, mul(hd, h[i][c]), i + 1);
        }
      }
    }

    int solve(long long n) {
      global_n = n;
      ans = 0;
      dfs(1, 1, 1);
      return ans;
    }
    }  // namespace PNS

    int main() {
      PNS::init();
      long long n;
      cin >> n;
      cout << PNS::solve(n) << '\n';
      return 0;
    }

### 「LOJ #6053」简单的函数

给定 𝑓(𝑛)f(n)：

𝑓(𝑛)=⎧{ {⎨{ {⎩1 𝑛=1 𝑝⊕𝑐 𝑛=𝑝 𝑐 𝑓(𝑎)𝑓(𝑏)𝑛=𝑎 𝑏 a n d 𝑎⟂𝑏 f(n)={1 n=1 p⊕c n=p c f(a)f(b)n=a b and a⟂b

易得：

𝑓(𝑝)={𝑝+1 𝑝=2 𝑝−1 o t h e r w i s e f(p)={p+1 p=2 p−1 otherwise

构造 𝑔 g 为

𝑔(𝑛)={3 𝜑(𝑛)2∣𝑛 𝜑(𝑛)o t h e r w i s e g(n)={3 φ(n)2∣n φ(n)otherwise

易证 𝑔(𝑝) =𝑓(𝑝)g(p)=f(p) 且 𝑔 g 为积性函数．

下面考虑求 𝐺(𝑛)G(n)．

𝐺(𝑛)=𝑛∑𝑖=1[𝑖 m o d 2=1]𝜑(𝑖)+3 𝑛∑𝑖=1[𝑖 m o d 2=0]𝜑(𝑖)=𝑛∑𝑖=1 𝜑(𝑖)+2 𝑛∑𝑖=1[𝑖 m o d 2=0]𝜑(𝑖)=𝑛∑𝑖=1 𝜑(𝑖)+2⌊𝑛 2⌋∑𝑖=1 𝜑(2 𝑖)G(n)=∑i=1 n[i mod 2=1]φ(i)+3∑i=1 n[i mod 2=0]φ(i)=∑i=1 n φ(i)+2∑i=1 n[i mod 2=0]φ(i)=∑i=1 n φ(i)+2∑i=1⌊n 2⌋φ(2 i)

记 𝑆 1(𝑛) =∑𝑛 𝑖=1 𝜑(𝑖)S 1(n)=∑i=1 n φ(i)，𝑆 2(𝑛) =∑𝑛 𝑖=1 𝜑(2 𝑖)S 2(n)=∑i=1 n φ(2 i)，则 𝐺(𝑛) =𝑆 1(𝑛) +2 𝑆 2(⌊𝑛 2⌋)G(n)=S 1(n)+2 S 2(⌊n 2⌋)．

当 2 ∣𝑛 2∣n 时，有

𝑆 2(𝑛)=𝑛∑𝑖=1 𝜑(2 𝑖)=𝑛 2∑𝑖=1(𝜑(2(2 𝑖−1))+𝜑(2(2 𝑖)))=𝑛 2∑𝑖=1(𝜑(2 𝑖−1)+2 𝜑(2 𝑖))=𝑛 2∑𝑖=1(𝜑(2 𝑖−1)+𝜑(2 𝑖))+𝑛 2∑𝑖=1 𝜑(2 𝑖)=𝑛∑𝑖=1 𝜑(𝑖)+𝑆 2(𝑛 2)=𝑆 1(𝑛)+𝑆 2(⌊𝑛 2⌋)S 2(n)=∑i=1 n φ(2 i)=∑i=1 n 2(φ(2(2 i−1))+φ(2(2 i)))=∑i=1 n 2(φ(2 i−1)+2 φ(2 i))=∑i=1 n 2(φ(2 i−1)+φ(2 i))+∑i=1 n 2 φ(2 i)=∑i=1 n φ(i)+S 2(n 2)=S 1(n)+S 2(⌊n 2⌋)

当 2 ∤𝑛 2∤n 时，有

𝑆 2(𝑛)=𝑆 2(𝑛−1)+𝜑(2 𝑛)=𝑆 2(𝑛−1)+𝜑(𝑛)=𝑛−1∑𝑖=1 𝜑(𝑖)+𝑆 2(𝑛−1 2)+𝜑(𝑛)=𝑆 1(𝑛)+𝑆 2(⌊𝑛 2⌋)S 2(n)=S 2(n−1)+φ(2 n)=S 2(n−1)+φ(n)=∑i=1 n−1 φ(i)+S 2(n−1 2)+φ(n)=S 1(n)+S 2(⌊n 2⌋)

综上，有 𝑆 2(𝑛) =𝑆 1(𝑛) +𝑆 2(⌊𝑛 2⌋)S 2(n)=S 1(n)+S 2(⌊n 2⌋)．

𝑆 1 S 1 可以用杜教筛求，𝑆 2 S 2 直接按照公式推，这样 𝐺 G 也可以求出来了．

参考代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    #include <iostream>
    #include <map>
    using namespace std;

    constexpr int MOD = 1e9 + 7;
    constexpr int inv2 = (MOD + 1) / 2;

    template <typename T>
    int mint(T x) {
      x %= MOD;
      if (x < 0) x += MOD;
      return x;
    }

    int add(int x, int y) {
      return x + y >= MOD ? x + y - MOD : x + y;  // 防止大于模数
    }

    int mul(int x, int y) { return (long long)1 * x * y % MOD; }

    int sub(int x, int y) {
      return x < y ? x - y + MOD : x - y;  // 防负数
    }

    namespace PNS {
    constexpr int N = 2e6 + 5;
    constexpr int M = 35;

    long long global_n;

    int s1[N], s2[N];

    int h[N][M];
    bool vis_h[N][M];

    int ans;

    int pcnt, prime[N], phi[N];
    bool isp[N];

    void sieve(int n) {
      pcnt = 0;
      for (int i = 2; i <= n; ++i) isp[i] = true;  // 判断质数数组
      phi[1] = 1;
      for (int i = 2; i <= n; ++i) {
        if (isp[i]) {
          ++pcnt;
          prime[pcnt] = i;
          phi[i] = i - 1;
        }
        for (int j = 1; j <= pcnt; ++j) {  // 筛去非质数
          long long nxt = (long long)1 * i * prime[j];
          if (nxt > n) break;
          isp[nxt] = false;
          if (i % prime[j] == 0) {  // i是非质数的情况
            phi[nxt] = phi[i] * prime[j];
            break;
          }
          phi[nxt] = phi[i] * phi[prime[j]];
        }
      }

      s1[0] = 0;
      for (int i = 1; i <= n; ++i) s1[i] = add(s1[i - 1], phi[i]);

      s2[0] = 0;
      for (int i = 1; i <= n / 2; ++i) {
        s2[i] = add(s2[i - 1], phi[2 * i]);
      }
    }

    void init() {
      sieve(N - 1);
      for (int i = 1; i <= pcnt; ++i) h[i][0] = 1;
      for (int i = 1; i <= pcnt; ++i) vis_h[i][0] = true;
    }

    map<long long, int> mp_s1;

    int S1(long long n) {
      if (n < N) return s1[n];
      if (mp_s1.count(n)) return mp_s1[n];

      int ret = mul(mul(mint(n), mint(n + 1)), inv2);
      for (long long i = 2, j; i <= n; i = j + 1) {
        j = n / (n / i);
        ret = sub(ret, mul(mint(j - i + 1), S1(n / i)));
      }
      mp_s1[n] = ret;
      return ret;
    }

    map<long long, int> mp_s2;

    int S2(long long n) {
      if (n < N / 2) return s2[n];
      if (mp_s2.count(n)) return mp_s2[n];
      int ret = add(S1(n), S2(n / 2));
      mp_s2[n] = ret;
      return ret;
    }

    int G(long long n) { return add(S1(n), mul(2, S2(n / 2))); }

    void dfs(long long d, int hd, int pid) {
      ans = add(ans, mul(hd, G(global_n / d)));

      for (int i = pid; i <= pcnt; ++i) {
        if (i > 1 && d > global_n / prime[i] / prime[i]) break;  // 剪枝

        int c = 2;
        for (long long x = d * prime[i] * prime[i]; x <= global_n;
             x *= prime[i], ++c) {
          if (!vis_h[i][c]) {
            int f = prime[i] ^ c, g = prime[i] - 1;

            // p = 2时特判一下
            if (i == 1) g = mul(g, 3);

            for (int j = 1; j <= c; ++j) {
              f = sub(f, mul(g, h[i][c - j]));
              g = mul(g, prime[i]);
            }
            h[i][c] = f;
            vis_h[i][c] = true;
          }

          if (h[i][c]) dfs(x, mul(hd, h[i][c]), i + 1);
        }
      }
    }

    int solve(long long n) {
      global_n = n;
      ans = 0;
      dfs(1, 1, 1);
      return ans;
    }
    }  // namespace PNS

    int main() {
      PNS::init();  // 预处理函数
      long long n;
      cin >> n;
      cout << PNS::solve(n) << '\n';
      return 0;
    }

## 习题

- PE708 Twos are all you need
- PE639 Summing a multiplicative function
- PE484 Arithmetic Derivative

## 参考资料

- 破壁人五号 - Powerful number 筛略解
- command_block - 杜教筛（+ 贝尔级数 + powerful number）

> 本页面最近更新：2026/1/7 08:56:54，更新历史
> 发现错误？想一起完善？ 在 GitHub 上编辑此页！
> 本页面贡献者：Enter-tainer, Tiphereth-A, Backl1ght, StudyingFather, Xeonacid, cy1999, Great-designer, iamtwz, Ir1d, kenlig, ksyx, LaDeXX, lrherqwq, Marcythm, xyf007
> 本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供，附加条款亦可能应用