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

Min_25 筛学习笔记

时间:2024-08-30 11:04:36浏览次数:9  
标签:lfloor 25 frac Min 质数 求解 笔记 rfloor 合数

\(\text{Min}\_25\) 筛学习笔记

事实上我又学习了一个有点春的筛法。\(\text{Min}\_25\) 筛用于求解积性函数的前缀和,即形如 \(g(n)=\sum_{i=1}^{n}f(i)\) 形式的函数 \(g\)。

众所周知,朴素筛法之所以无法做到低于线性是因为枚举了区间内的每一个数,那么我们想要做到低于线性,就必然需要做到通过一种方法将所有数字分成两类,用过一类的求解辅助另一类的求解,不难想到质数和合数。那么我们分开考虑如何求解质数和合数。对于 \(1\) 这个既不是质数也不是合数的数,我们选择将其的贡献最后单独统计,毕竟一个积性函数要么所有位置都是 \(0\),要么 \(f(1)=1\)。

质数求解

首先考虑如何求解质数的贡献。众所周知,幂函数是一种完全积性函数,假若当 \(x=p\) 时 \(f(x)=\sum x^i\),那么我们便可以将每一项分开求解。更具体的我们尝试统计 \(\sum_{i\in P}i^k\) 的值。

埃筛是一种复杂度略差于线性筛的筛法,但只表现为拥有较大的常数,在这里我们借鉴埃筛的思路。考虑埃筛利用每一个范围内的质数进行过滤,更具体的,对于每一个质数 \(p\),其都将 \(\ge p^2\) 且\(\bmod p=0\) 的数筛掉。那么我们考虑所有 \(\le n\) 的合数一定有一个 \(\le\sqrt{n}\) 的质因子,因此我们只需要筛出前 \(\sqrt{n}\) 个素数即可。但是埃筛的时间复杂度依旧不可接受。考虑用 \(\text{dp}\) 优化这个过程,我们设 \(g_{i,j}\) 表示 \([1,i]\) 之间的数在经过 \(j\) 轮筛之后所剩余的数的贡献,那么我们需要知道的就是 \(g_{n,|P_{\sqrt n}|}\)。首先有初始值

\[g_{n,0}=\sum_{i=2}^{n}i^k \]

那么考虑因为质数 \(p_j\) 只会筛掉 \(\ge p_j^2\) 的数,那么我们可以推断出转移方程

\[g_{i,j}=g_{i,j-1}(i<p_j^2) \]

接着考虑 \(i\ge p_j^2\) 的时候,因为完全积性函数的性质,所以我们单独将被筛掉的合数的质因子 \(p_j\) 提出来。那么剩下的部分应该是最小质因子 \(\ge p_j\) 的部分,因为如果最小质因子 \(<p_j\) 一定会被之前的某个素数筛掉,我们可以用 \(g_{\lfloor\frac{i}{p_j},j-1\rfloor}\) 来表示,但是这里面包含了 \([1,p_{j-1}]\) 的质数,不应该减去这部分,因此还需要将这部分贡献加回来,所以有转移方程

\[g_{i,j}=g_{i,j-1}-p_j^k(g_{\lfloor\frac{i}{p_j},j-1\rfloor}-g_{p_{j-1},j-1})(i\ge p_j^2) \]

不难发现可以用滚动数组优化掉第二维,但是时空复杂度依旧没有消下去。首先后半部分其实没有必要利用 \(g\) 数组来表示,反倒可以利用素数前缀和数组 \(s\),而这个数组后面也会遇到。接着考虑可能被转移的位置 \(\lfloor\frac{i}{p_j}\rfloor\),如果从搜索的角度看,就是 \(n,\lfloor\frac{n}{p_1}\rfloor,\lfloor\frac{\lfloor\frac{n}{p_1}\rfloor}{p_2}\rfloor,\cdots\),有一个化简技巧就是 \(\lfloor\frac{\lfloor\frac{n}{a}\rfloor}{b}\rfloor=\lfloor\frac{n}{ab}\rfloor\)。那么我们发现有用的下标其实只有 \(\lfloor\frac{n}{a}\rfloor\),那么我们可以利用数论分块在 \(O(\sqrt{n})\) 的时间复杂度内处理出所有可能的值。

合数求解

在求解完素数之后我们就要酝酿着统计合数对答案的贡献了。因为给定的函数是积性函数的缘故,因此我们可以将合数表示为最小质因子的次方和另一个数相乘的结果,因为后半部分的最小质因子不能小于原来的最小质因子,因此我们令 \(S(n,b)\) 表示范围 \([1,n]\) 内所有最小质因子 \(>p_b\) 的数的贡献和,那么所求即为 \(S(n,0)\)。递归求解这个东西。当 \(n\le p_b\) 时,直接返回 \(0\) 即可。否则我们先统计所有素数的答案,然后进行最小质因子和其次幂的枚举,并将对应的答案贡献累加,更具体的,有转移方程

\[S(n,b)=g_{n,|P_{\sqrt{n}}|}-\sum_{i=1}^{b}f(p_i)+\sum_{i=b+1}^{|P_{\sqrt{n}}|}\sum_{1\le j,p_i^{j}\le n}(S(\lfloor\frac{n}{p_i^{j}}\rfloor,i)+[j\ne1]f(p_i^j)) \]

我们发现中间的求和可以用之前的质数前缀和快速求解,而剩余部分自行递归即可。整体的复杂度是 \(O(\frac{n^{\frac{3}{4}}}{\log n})\)。

\(\text{Min}\_25\) 筛的使用场景包括但不限于所有可以快速求出质数及其幂次处的函数值,并且其在质数处的取值可以视作关于 \(p\) 的多项式。

事实上在代码实现时,有一些不加不可的优化,和一些美化代码的优化,只能说自行选择了。代码以 \(f(x)=x\) 作为演示

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define Get(x) (x<=S?id1[x]:id2[n/(x)])
#define f(p) (p)

const int N=1e5+5,P=1e9+7;

int S,cnt,tot;
int p[N],id1[N],id2[N];
ll n;
ll sum[N],g[N<<1],val[N<<1];//n/i 的值最多有 2sqrt(n) 个,结论不要记错了
bool isp[N];

void Euler_S(){
	isp[1]=true;
	for(int i=2;i<=S;i++){
		if(!isp[i]){
			p[++cnt]=i;
			sum[cnt]=(sum[cnt-1]+i)%P;
		}
		for(int j=1;p[j]<=S/i;j++){
			isp[i*p[j]]=true;
			if(i%p[j]==0)continue;
		}
	}
	return ;
}

ll SumF(ll a,int b){
	if(a<=p[b])return 0;
	int id=Get(a);
    ll res=g[id]-sum[b];
	for(int i=b+1;i<=cnt&&p[i]<=a/p[i];i++){//注意这里后半部分的判断是必加的,不加也能够得出正确答案,但因为常数过大被卡爆了
		ll pk=p[i];
		for(int j=1;pk<=a/p[i];j++,pk*=p[i])res=(res+f(pk)*SumF(a/pk,i)+f(pk*p[i]))%P;
        //这部分和上面所说的转移方程略有不同,因为考虑到如果 pk*p>a,那么调用过去答案一定为 0,那么就没有必要进行这次操作
        //并且后半部分变成 f(p^(j+1)),这样既能够避免重复统计质数,也能刚好覆盖到最大的数,同时能够保障 pk 不超过 ll 范围
        //这部分优化可以自行选择
	}
	return res;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	S=sqrt(n);
	Euler_S();
	for(ll l=1,r,v;l<=n;l=r+1){
		r=n/(v=n/l);
		if(v<=S)id1[v]=++tot;
		else id2[n/v]=++tot;//两种分开存储
		val[tot]=v;
		v%=P;
		g[tot]=v*(v+1)/2%P-1;//别忘了初始值没有包括 1
	}
	for(int j=1;j<=cnt;j++){
		for(int i=1;i<=tot&&p[j]<=val[i]/p[j];i++){
			int id=Get(val[i]/p[j]);
			g1[i]=(g1[i]-(g1[id]-(j-1)))%P;
			g2[i]=(g2[i]-p[j]*(g2[id]-sum[j-1]))%P;
		}
	}
	cout<<(SumF(n,0)+1+P)%P;//记得单独加上 1 的贡献
	return 0;
}

标签:lfloor,25,frac,Min,质数,求解,笔记,rfloor,合数
From: https://www.cnblogs.com/DycBlog/p/18388307

相关文章

  • MuJoCo 学习笔记:简介 Overview
    MuJoCo官方文档给出了详细介绍MuJoCoOverview。下面截取部分相对重要的内容翻译记录。参考:Mujoco官方文档中文翻译1-概述1.KeyFeature广义坐标+现代接触动力学Generalizedcoordinatescombinedwithmoderncontactdynamics物理引擎传统上分为两类。1.机器人学和......
  • Java学习笔记11-流程控制语句结构
    一.顺序结构顺序结构顺序结构是最简单的流程控制结构,它按照代码书写的顺序依次执行每一条语句。例如:inta=1,b=2,c=3;System.out.println("a+b="+(a+b));System.out.println("b*c="+(b*c));二.分支结构if分支判断(1).单if条件判断if(条件,条件的......
  • huawei0821笔试第二题笔记:双端队列deque
    intsolve(deque<int>machines,constvector<int>&tasks){for(inttask:tasks){intcnt=0;//首件不匹配while(cnt<machines.size()&&task!=machines.front()){machines.push_back(machines.......
  • Datawhale X 李宏毅苹果书(入门) AI夏令营 task02笔记
    官方学习文档:https://linklearner.com/activity/16/14/55往期task01链接:https://mp.csdn.net/mp_blog/creation/editor/141535616李宏毅老师对应视频课程可供食用:https://www.bilibili.com/video/BV1JA411c7VT/?p=3机器学习基础线性模型        w跟b的值上期ta......
  • Datawhale X 李宏毅苹果书(入门) AI夏令营 task01笔记
    官方学习链接:https://linklearner.com/activity/16/14/42机器学习基础导读        通俗来讲,机器学习就是让机器具备找一个函数的能力。这里指的“找一个函数”,指的是找一个能够描述一个场景数学规律的函数模型,具体方法大致是:让机器运行算法,通过输入的数据,确定合适的......
  • C语言详细笔记--构造数据类型(结构体数组)
    目录一、结构体数组的定义二、结构体数组的初始化三、结构体数组的引用一、结构体数组的定义structstuscoretype{intstuid;intscore[3];doubleaverage;};structstuscoretypestu[3];上面语句定义了一个名为stu的数组,数组有三个元素,每个元素的类......
  • 学习笔记:robots.txt文件
    1.1介绍robots.txt文件是一种用于指导搜索引擎爬虫在网站上哪些页面可以被抓取,哪些页面不应该被抓取的文本文件。这个文件通常放置在网站的根目录下。1.2由来robots.txt标准最早出自1994年,由荷兰的网络管理员和搜索引擎专家MartijnKoster编写。最初称为"RobotsExclusion"......
  • L2-024 部落 分数 25
    基础款并查集练习题//13'11"#include<bits/stdc++.h>usingnamespacestd;constintN=1e4+10;intp[N],f[N];voidinit(){for(inti=1;i<=N;++i)p[i]=i;}intfind(intx){if(x!=p[x])p[x]=find(p[x]);ret......
  • L2-013 红色警报 分数 25
    时间复杂度N*M≈2.5e6#include<bits/stdc++.h>usingnamespacestd;intn=510,m;constintN=510;vector<pair<int,int>>path;//储存所有边intp[N];//储存祖宗节点boolflag[N];//判断是否已经被去除voidinit()//初始化所有祖宗节点{......
  • CMake构建学习笔记11-minizip库的构建
    准确来说,minizip其实是zlib提供的辅助工具,位于zlib库的contrib文件夹内。minizip提供了更为高级一点的接口,能直接操作文件进行压缩。不过,有点麻烦的是这个工具并没有提供CMake构建的方式。那么可以按照构建giflib的方式,自己组织CMakeList.txt,正好这个项目的代码量并不多。另一个......