首页 > 其他分享 >杜教筛学习笔记

杜教筛学习笔记

时间:2023-08-26 13:45:21浏览次数:34  
标签:right frac sum 笔记 杜教 学习 sqrt operatorname left

杜教筛学习笔记

闲话

感觉以前根本没学懂杜教筛,于是重学了一遍,写个笔记记录一下。

前置知识

依赖于迪利克雷卷积、莫比乌斯反演、整除分块相关知识。

记号约定及基本性质

约定:

  • \(f*g\) 表示 \(f\) 与 \(g\) 的迪利克雷卷积,即 \((f*g)(n)=\sum\limits_{ij=n}f(i)g(j)\)。
  • \(f\cdot g\) 表示 \(f\) 与 \(g\) 的点积,即 \((f\cdot g)(n)=f(n)g(n)\)。
  • \(f^k\) 表示 \(f\) 点积的幂次,即 \(f^k=\underbrace{f\cdot f\cdot\cdots\cdot f}_{k\textrm{ times}}\)。
  • \(S_f\) 表示 \(f\) 的前缀和,即 \(S_f(n)=\sum\limits_{i=1}^nf(i)\)。
  • \(\epsilon(x)=[x=1]\)。
  • \(\operatorname{I}(x)=1\)。
  • \(\operatorname{id}(x)=x\)。
  • \(\varphi\) 为欧拉函数,即 \(\varphi(n)=\sum\limits_{i=1}^n[i\perp n]\)。
  • \(\mu\) 为莫比乌斯函数,是 \(\operatorname{I}\) 的反函数,设 \(n=\prod_{i=1}^kp_i^{\alpha_i}\),即 \(\mu(n)=\begin{cases}1,&n=1\\0,&\exists i,\alpha_i\ge 2\\(-1)^k,&\forall i,\alpha_i=1\end{cases}\)。
  • \(\sigma_k\) 为因数幂和函数,即 \(\sigma_k(n)=\sum\limits_{d\mid n}d^k\)。特别地,\(\sigma_0\) 为因数个数函数,\(\sigma_1\) 为因数和函数。

有性质:

  • \(\varphi*\operatorname{I}=\operatorname{id}\),即 \(\varphi=\mu*\operatorname{id}\)。
  • \(\mu*\operatorname{I}=\epsilon\)。
  • \((f\cdot g)*(f\cdot h)=f\cdot(g*h)\)。

杜教筛算法流程

杜教筛用于求一类数论函数的前缀和,并不要求积性

假设要求 \(S_f(n)\),如果构造出数论函数 \(g\) 满足 \(S_g,S_h\) 可以快速求出(记 \(h=f*g\)),即可快速计算出 \(S_f(n)\)。

进行以下推导:

\[\begin{aligned} S_h(n)&=\sum_{i=1}^nh(i)\\ &=\sum_{i=1}^n\sum_{d\mid i}f\left(\frac{i}{d}\right)g(d)\\ &=\sum_{d=1}^n\sum_{i=1}^{\frac{n}{d}}f(i)g(d)\\ &=\sum_{d=1}^ng(d)S_f\left(\left\lfloor\frac{n}{d}\right\rfloor\right)\\ &=g(1)S_f(n)+\sum_{d=2}^ng(d)S_f\left(\left\lfloor\frac{n}{d}\right\rfloor\right)\\ S_f(n)&=\frac{1}{g(1)}\left(S_h(n)-\sum_{d=2}^ng(d)S_f\left(\left\lfloor\frac{n}{d}\right\rfloor\right)\right) \end{aligned} \]

利用可以快速求出的 \(S_h\) 和 \(S_g\),可以整除分块求解。

复杂度证明

显然只会递归求 \(S_f(\lfloor\frac{n}{d}\rfloor)\) 的值,共 \(O(\sqrt{n})\) 个,可以记忆化之。

复杂度为:

\[\begin{aligned} T(n)&=O\left(\sum_{i=1}^{\sqrt{n}}\sqrt{i}+\sum_{i=1}^{\sqrt{n}}\sqrt{\left\lfloor\frac{n}{i}\right\rfloor}\right)\\ &=O\left(\sum_{i=1}^{\sqrt{n}}\sqrt{\left\lfloor\frac{n}{i}\right\rfloor}\right)\\ &=O\left(\int_1^{\sqrt{n}}\sqrt{\frac{n}{x}}\textrm{d}x\right)\\ &=O\left(n^{\frac{3}{4}}\right) \end{aligned} \]

如果我们通过线性筛等方式预处理出 \(1\sim n^c\) 的答案(其中 \(c > \frac{1}{2}\)),那么只需要递归计算 \(\lfloor\frac{n}{1}\rfloor,\lfloor\frac{n}{2}\rfloor,\cdots,\lfloor\frac{n}{n^{1-c}}\rfloor\),复杂度为:

\[\begin{aligned} T(n)&=O\left(n^c+\sum_{i=1}^{n^{1-c}}\sqrt{\left\lfloor\frac{n}{i}\right\rfloor}\right)\\ &=O\left(n^c+\int_1^{n^{1-c}}\sqrt{\frac{n}{x}}\textrm{d}x\right)\\ &=O\left(n^c+n^{1-\frac{1}{2}c}\right)\\ \end{aligned} \]

令 \(c=\frac{2}{3}\) 时达到最优复杂度 \(T(n)=O(n^{\frac{2}{3}})\)。实际应用时,考虑到多测等原因,一般不预处理到 \(O(n^{\frac{2}{3}})\),而是预处理到可接受的范围(例如 \(10^7\))。

记忆化时使用 map 的话多一个 \(\log\),如果利用整除分块的性质在数组里记忆化,就不会带 \(\log\)。

杜教筛套杜教筛

有时求 \(S_g,S_h\) 时也需要进行杜教筛,但是不会影响复杂度,因为都只会递归求 \(\lfloor\frac{n}{d}\rfloor\) 处的值,总复杂度是相加关系。

例题

P4213 【模板】杜教筛(Sum)

单点求 \(S_\varphi\) 和 \(S_\mu\)。

对于 \(S_\varphi\),使用 \(\varphi*\operatorname{I}=\operatorname{id}\)。显然有 \(S_{\operatorname{I}}(n)=n\),\(S_{\operatorname{id}}(n)=\frac{n(n+1)}{2}\)。

对于 \(S_\mu\),使用 \(\mu*\operatorname{I}=\epsilon\)。显然有 \(S_{\operatorname{I}}(n)=n\),\(S_\epsilon(n)=1\)。

**代码**
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x,y,z) for(ll x=(y);x<=(z);x++)
#define per(x,y,z) for(ll x=(y);x>=(z);x--)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
ll randint(ll L, ll R) {
    uniform_int_distribution<ll> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

const ll N = 1e7+5;

ll T, n, p[N / 10], pcnt, sphi[N], smu[N];
bool tab[N];
map<ll, ll> Sphi, Smu;

void sieve(ll lim) {
    sphi[1] = 1;
    smu[1] = 1;
    rep(i, 2, lim) {
        if(!tab[i]) {
            p[++pcnt] = i;
            sphi[i] = i - 1;
            smu[i] = -1;
        }
        for(ll j = 1; j <= pcnt && i * p[j] <= lim; j++) {
            tab[i * p[j]] = true;
            if(i % p[j] != 0) {
                sphi[i * p[j]] = sphi[i] * sphi[p[j]];
                smu[i * p[j]] = -smu[i];
            }
            else {
                sphi[i * p[j]] = sphi[i] * p[j];
                smu[i * p[j]] = 0;
                break;
            }
        }
    }
    rep(i, 2, lim) {
        sphi[i] += sphi[i-1];
        smu[i] += smu[i-1];
    }
}

ll djs_phi(ll x) {
    if(x < N) return sphi[x];
    if(Sphi.count(x)) return Sphi[x];
    ll ans = x * (x + 1) / 2;
    for(ll L = 2, R; L <= x; L = R + 1) {
        R = x / (x / L);
        ans -= (R - L + 1) * djs_phi(x / L);
    }
    return Sphi[x] = ans;
}

ll djs_mu(ll x) {
    if(x < N) return smu[x];
    if(Smu.count(x)) return Smu[x];
    ll ans = 1;
    for(ll L = 2, R; L <= x; L = R + 1) {
        R = x / (x / L);
        ans -= (R - L + 1) * djs_mu(x / L);
    }
    return Smu[x] = ans;
}

int main() {
    sieve(N-1);
    for(scanf("%lld", &T); T; T--) {
        scanf("%lld", &n);
        printf("%lld %lld\n", djs_phi(n), djs_mu(n));
    }
    return 0;
}

标签:right,frac,sum,笔记,杜教,学习,sqrt,operatorname,left
From: https://www.cnblogs.com/ruierqwq/p/du-sieve.html

相关文章

  • 文章学习 | 大模型发展
    嬗变:大语言模型带来的人工智能新纪元|CCCF精选盖茨说:大语言模型创新的影响力可以与20世纪60年代的微处理器、80年代的个人电脑、90年代的互联网和21世纪初的苹果手机媲美。大模型的创新大语言模型是人工智能领域自然语言处理的一部分。在大语言模型出现之前,自然语言处理主......
  • 前端React学习路径
    在当今的软件开发领域,React已经成为一种广泛使用的JavaScript库,用于构建用户界面。它由Facebook开发并维护,具有高效、灵活和可扩展等特点,适用于各种类型的应用程序开发。本文将介绍前端React的学习路径,包括基本概念、核心功能、组件进阶、路由和状态管理、构建实践等方面,并结合代码......
  • 8.21-8.27学习总结博客七:Spark机器学习与实时处理
    博客题目:学习总结七:Spark机器学习与实时处理入门内容概要:学习使用Spark进行机器学习和实时数据处理的基本知识,了解Spark的机器学习库和实时处理框架。学习资源:推荐的Spark机器学习和实时处理教程、案例和学习资源。实践内容:通过编写Spark应用程序,实践使用Spark进行机器学习和实时......
  • Linux设备驱动开发详解——学习笔记
    Linux设备驱动概述计算机系统的运转需要软件和硬件共同参与,硬件是底层基础,软件则实现了具体的应用。硬件和软件之间则通过设备驱动来联系。在没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口。而在有操作系统的情况下,驱动的架构则由相应的操作系统来定义。驱动存......
  • 暂告一段落的vue学习以及uniapp的开始
    最近学习了VUE并且参照其他开源项目做了一个大致的框架,只余部分还需要改成自己的东西,但是这个要到一段落了,开始了小程序项目之旅,首先还是到uniapp官网查看官方资料当然了,还是要从vue3的部分开始学起跟着官方视频做了一个试例,感觉还不错,很简单然后项目正式开始了~~终于开始了......
  • 项目学习 鱼皮 API 开放平台 stateful-backend 项目总结
    项目介绍用户中心功能介绍提供了一套基于Session的用户中心,提供以下功能登入登出注册用户信息管理增删改查登录用户查询ID查询列表查询分页查询用户态记录数据库表createtableuser(idbigintauto_incrementcomment'id'......
  • Mybatis学习笔记
    一、Mybatis简介二、下载与实现1)下载 官网下载2)实现①创建项目工程,并加入依赖②创建核心configuration.xml配置文件,配置JDBC的连接信息③创建POJO对象④创建POJO对应的mapper映射文件⑤在configuration.xml文件中加载mapper文件⑥测试三、接口实现方式(项目中常用)①创建一个接口②......
  • 运筹学习笔记之列生成
    列生成算法介绍1.什么是列生成列生成算法是一种用于解决大规模线性规划问题的高效算法,它基于单纯形法的思想,通过求解子问题来找到可以进基的非基变量。在列生成算法中,每个变量都代表一列,因此称为列生成算法。该算法的优点在于其高效的计算性能和较好的收敛性,适用于处理大规模、......
  • 学习IDA权威指南-反汇编器/调试器集成
    1-启动调试器依附进程进行调试分析2-调试器的基本显示3-进程控制3-1断点3-2跟踪3-3栈跟踪3-4监视调试器任务自动化调试器操作自动化IDA数据库与IDA调试器调试模糊代码启用附加进程简单的解密和解压循环导入表重建隐藏调试器异常处理......
  • NumPy学习挑战第十二关-数组操作
    Numpy数组操作Numpy中包含了一些函数用于处理数组,大概可分为以下几类:1、修改数组形状函数 描述reshape 不改变数据的条件下修改形状flat 数组元素迭代器flatten 返回一份数组拷贝,对拷贝所做的修改不会影响原始数组ravel 返回展开数组(1)numpy.reshapenumpy.reshape函数......