数论函数前缀和问题之杜教筛

文章讲述了如何利用数论技巧,如线性筛和数论分块,推导φ(n)和μ(n)的和的递推公式,并通过记忆化搜索实现O(n^(2/3))的时间复杂度计算。给出了φ(n)和μ(n)的实例以及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在阅读本文前假设你已经掌握了线性筛,数论分块,常用数论函数和狄利克雷卷积

题目描述

给定一个正整数 n n n,求 ∑ i = 1 n φ ( i ) \sum_{i=1}^{n}\varphi(i) i=1nφ(i) ∑ i = 1 n μ ( i ) \sum_{i=1}^{n}\mu(i) i=1nμ(i),其中 1 ≤ n ≤ 1 0 9 1\le n\le 10^9 1n109

推导过程

S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum_{i=1}^{n}f(i) S(n)=i=1nf(i)

对于任意一个数论函数 g ( n ) g(n) g(n),满足: ∑ i = 1 n ( f ∗ g ) ( i ) = ∑ i = 1 n g ( i ) S ( ⌊ n i ⌋ ) \sum_{i=1}^{n}(f*g)(i)=\sum_{i=1}^{n}g(i)S(\left\lfloor \frac{n}{i} \right\rfloor) i=1n(fg)(i)=i=1ng(i)S(in)

证明:

∑ i = 1 n ( f ∗ g ) ( i ) = ∑ i = 1 n ∑ x y = i f ( x ) g ( y ) = ∑ x y ≤ n f ( x ) g ( y ) = ∑ y = 1 n ∑ x = 1 ⌊ n y ⌋ f ( x ) g ( y ) = ∑ y = 1 n g ( y ) ∑ x = 1 ⌊ n y ⌋ f ( x ) = ∑ y = 1 n g ( y ) S ( ⌊ n y ⌋ ) = ∑ i = 1 n g ( i ) S ( ⌊ n i ⌋ ) \begin{aligned} \sum_{i=1}^{n}(f*g)(i) &= \sum_{i=1}^{n}\sum_{xy=i}f(x)g(y) \\ &= \sum_{xy\le n}f(x)g(y) \\ &= \sum_{y=1}^{n}\sum_{x=1}^{\left\lfloor \frac{n}{y} \right\rfloor}f(x)g(y) \\ &= \sum_{y=1}^{n}g(y)\sum_{x=1}^{\left\lfloor \frac{n}{y} \right\rfloor}f(x) \\ &= \sum_{y=1}^{n}g(y)S(\left\lfloor \frac{n}{y} \right\rfloor) \\ &= \sum_{i=1}^{n}g(i)S(\left\lfloor \frac{n}{i} \right\rfloor) \end{aligned} i=1n(fg)(i)=i=1nxy=if(x)g(y)=xynf(x)g(y)=y=1nx=1ynf(x)g(y)=y=1ng(y)x=1ynf(x)=y=1ng(y)S(yn)=i=1ng(i)S(in)

利用上式构造 S ( n ) S(n) S(n) 关于 ∑ i = 2 n S ( ⌊ n i ⌋ ) \sum_{i=2}^{n}S(\left\lfloor\frac{n}{i}\right\rfloor) i=2nS(in) 的递推式。

∑ i = 1 n ( f ∗ g ) ( i ) = g ( 1 ) S ( n ) + ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) \sum_{i=1}^{n}(f*g)(i)=g(1)S(n)+\sum_{i=2}^{n}g(i)S(\left\lfloor\frac{n}{i}\right\rfloor) i=1n(fg)(i)=g(1)S(n)+i=2ng(i)S(in)

g ( 1 ) S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) g(1)S(n)=\sum_{i=1}^{n}(f*g)(i)-\sum_{i=2}^{n}g(i)S(\left\lfloor\frac{n}{i}\right\rfloor) g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(in)

f ( n ) f(n) f(n) φ ( n ) \varphi(n) φ(n),我们有 ( φ ∗ 1 ) ( n ) = id ( n ) (\varphi*1)(n)=\text{id}(n) (φ1)(n)=id(n),可以令 g ( n ) g(n) g(n) 1 ( n ) 1(n) 1(n),所以 ( f ∗ g ) ( n ) = ( φ ∗ 1 ) ( n ) = id ( n ) (f*g)(n)=(\varphi*1)(n)=\text{id}(n) (fg)(n)=(φ1)(n)=id(n)

1 ( n ) S ( n ) = ∑ i = 1 n id ( i ) − ∑ i = 2 n 1 ( i ) S ( ⌊ n i ⌋ ) 1(n)S(n)=\sum_{i=1}^{n}\text{id}(i)-\sum_{i=2}^{n}1(i)S(\left\lfloor\frac{n}{i}\right\rfloor) 1(n)S(n)=i=1nid(i)i=2n1(i)S(in)

S ( n ) = n ( n + 1 ) 2 − ∑ i = 2 n S ( ⌊ n i ⌋ ) S(n)=\frac{n(n+1)}{2}-\sum_{i=2}^{n}S(\left\lfloor\frac{n}{i}\right\rfloor) S(n)=2n(n+1)i=2nS(in)

这里要预处理出 n 2 3 n^\frac{2}{3} n32 以内的 S ( n ) S(n) S(n),然后使用记忆化搜索和数论分块求解,时间复杂度是 O ( n 2 3 ) O(n^\frac{2}{3}) O(n32)

f ( n ) f(n) f(n) μ ( n ) \mu(n) μ(n),我们同样有 ( μ ∗ 1 ) ( n ) = ε ( n ) (\mu*1)(n)=\varepsilon(n) (μ1)(n)=ε(n),可以令 g ( n ) g(n) g(n) 1 ( n ) 1(n) 1(n),所以 ( f ∗ g ) ( n ) = ( μ ∗ 1 ) ( n ) = ε ( n ) (f*g)(n)=(\mu*1)(n)=\varepsilon(n) (fg)(n)=(μ1)(n)=ε(n)

1 ( n ) S ( n ) = ∑ i = 1 n ε ( i ) − ∑ i = 2 n 1 ( i ) S ( ⌊ n i ⌋ ) 1(n)S(n)=\sum_{i=1}^{n}\varepsilon(i)-\sum_{i=2}^{n}1(i)S(\left\lfloor\frac{n}{i}\right\rfloor) 1(n)S(n)=i=1nε(i)i=2n1(i)S(in)

S ( n ) = 1 − ∑ i = 2 n S ( ⌊ n i ⌋ ) S(n)=1-\sum_{i=2}^{n}S(\left\lfloor\frac{n}{i}\right\rfloor) S(n)=1i=2nS(in)

接下来跟上面的一样。

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned int
#define pii pair<int, int>
template <typename T> using vector2 = vector<vector<T>>;
template <typename T> using MaxHeap = priority_queue<T>;
template <typename T> using MinHeap = priority_queue<T, vector<T>, greater<T>>;
#define all(x) (x).begin(), (x).end()
#define endl '\n'
// #define endl " line in : " << __LINE__ << endl
const int N = 2e6 + 5, INF = 1e16, P = 998244353;
unordered_map<int, int> mp_phi, mp_mu;
int phi[N], mu[N];
vector<int> prm;
int vis[N];
int sol1(int n) {
  if (n < N) return phi[n];
  if (mp_phi.count(n))
    return mp_phi[n];
  int x = 0;
  for (int l = 2; l <= n; ) {
    int r = n / (n / l);
    x += sol1(n / l) * (r - l + 1);
    l = r + 1;
  }
  return mp_phi[n] = n * (n + 1) / 2 - x;
}
int sol2(int n) {
  if (n < N) return mu[n];
  if (mp_mu.count(n))
    return mp_mu[n];
  int x = 0;
  for (int l = 2; l <= n; ) {
    int r = n / (n / l);
    x += sol2(n / l) * (r - l + 1);
    l = r + 1;
  }
  return mp_mu[n] = 1 - x;
}
void test() {
  int n;
  cin >> n;
  cout << sol1(n) << ' ' << sol2(n) << endl;
}
signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  phi[1] = 1;
  mu[1] = 1;
  for (int i = 2; i < N; i++) {
    if (vis[i] == 0) {
      prm.push_back(i);
      phi[i] = i - 1;
      mu[i] = -1;
    }
    for (int p : prm) {
      if (i * p >= N)
        break;
      vis[i * p] = 1;
      if (i % p == 0) {
        phi[i * p] = phi[i] * p;
        mu[i * p] = 0;
        break;
      }
      phi[i * p] = phi[i] * phi[p];
      mu[i * p] = -mu[i];
    }
  }
  for (int i = 2; i < N; i++)
    phi[i] += phi[i - 1], mu[i] += mu[i - 1];
  int T; cin >> T; while (T--)
    test();
  return 0;
}

例题

洛谷 P4213 【模板】杜教筛

最后,送给大家一只。

文章太短了,下次再送。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值