●杜教筛入门(BZOJ 3944 Sum)

入门杜教筛啦。

http://blog.csdn.net/skywalkert/article/details/50500009(好文!)

可以在$O(N^{\frac{2}{3}})或O(N^{\frac{3}{4}})$的复杂度内解决求某些数论函数f(n)(或f的前缀和S(n)$)的值。

先来看看原理是什么。(接下来推导如何求数论函数f(n)的前缀和S(n))



现在有两个数论函数$f( )和g( )$

(同时定义f的前缀和函数$S(n)=\sum_{i=1}^{n}f(i)$)

有狄利克雷乘积可知:

$$f*g(n)=\sum_{i|n}f(\frac{n}{i})g(i)\quad(=\sum_{i|n}f(i)g(\frac{n}{i}))$$

那么,则有如下结论:

$$\sum_{n=1}^{N}f*g(n)=\sum_{i=1}^{N}g(i)S(\lfloor \frac{N}{i} \rfloor)$$

证明如下:

$$\begin{align*}
\sum_{n=1}^{N}f*g(n)&=\sum_{n=1}^{N}\sum_{i|n}f(\frac{n}{i})g(i)\\
&=\sum_{i=1}^{N}g(i)\sum_{i=1}^{\lfloor \frac{N}{i} \rfloor}f(i)\\
&=\sum_{i=1}^{N}g(i)S(\lfloor \frac{N}{i} \rfloor)
\end{align*}$$

然后把右边和式里的$g(1)S(N)$那一项提出来得到:

$$g(1)S(N)=\sum_{n=1}^{N}f*g(n)-\sum_{i=2}^{N}g(i)S(\lfloor \frac{N}{i} \rfloor)$$

通常令数论函数$g(n)=I(n)=1$(恒等函数$l(n)=1$,完全积性)

到目前为止,上式就是我们进行杜教筛的基础了。

因为左边的S(N)就是答案,而右边同时又可以用分块的方式计算。

不少刚刚入门的同学会有一个疑问,等式右边的后半部分确实可以分块计算,但是前半部分怎么办呢?

其实,一般前面的$\sum_{n=1}^{N}f*g(n)$都是可以O(1)计算出来的。

下面来看两个例子:



(一)、求莫比乌斯函数$\mu(n)$的前缀和函数$S(n),n \leq 10^9$

首先添加一个辅助函数g(n)=l(n)=1,

然后重复上面的过程,可以得到

$$g(1)S(N)=\sum_{n=1}^{N}\mu*g(n)-\sum_{i=2}^{N}g(i)S(\lfloor \frac{N}{i} \rfloor)$$

$$S(N)=\sum_{n=1}^{N}\mu*g(n)-\sum_{i=2}^{N}S(\lfloor \frac{N}{i} \rfloor)$$

现在来看看$\sum_{n=1}^{N}\mu*g(n)$怎么求:

$$\begin{aligned}
\sum_{n=1}^{N}\mu*g(n)&=\sum_{n=1}^{N}\sum_{i|n}\mu(i)g(\frac{n}{i})\\
&=\sum_{n=1}^{N}\sum_{i|n}\mu(i)\\
&=\sum_{n=1}^{N}[n==1]\\
&=1
\end{aligned}$$

上面的化简用到了刚刚学莫比乌斯函数时的一个结论:

$$\sum_{i|n}\mu(i)=[n==1]$$

到此,我们得到:

$$S(N)=1-\sum_{i=2}^{N}S(\lfloor \frac{N}{i} \rfloor)$$

实现方式是分块计算+记忆化递归处理(用map或者hash表记忆化)



(二). 求欧拉函数$\phi(n)$的前缀和函数$S(n),n \leq 10^9$

同样地,添加一个辅助函数g(n)=l(n)=1,

然后重复上面的过程,可以得到

$$g(1)S(N)=\sum_{n=1}^{N}\phi*g(n)-\sum_{i=2}^{N}g(i)S(\lfloor \frac{N}{i} \rfloor)$$

$$S(N)=\sum_{n=1}^{N}\phi*g(n)-\sum_{i=2}^{N}S(\lfloor \frac{N}{i} \rfloor)$$

$\sum_{n=1}^{N}\phi*g(n)$又怎样求呢:

$$\begin{aligned}
\sum_{n=1}^{N}\phi*g(n)&=\sum_{n=1}^{N}\sum_{i|n}\phi(i)g(\frac{n}{i})\\
&=\sum_{n=1}^{N}\sum_{i|n}\phi(i)\\
&=\sum_{n=1}^{N}n\\
&=\frac{(1+n)n}{2}
\end{aligned}$$

上面的化简用到了这样一个结论:

$$\sum_{i|n}\phi(i)=n$$

所以我们得到:

$$S(N)=\frac{(1+n)n}{2}-\sum_{i=2}^{N}S(\lfloor \frac{N}{i} \rfloor)$$

这个同样是实现方式是分块计算+记忆化递归处理(用map或者hash表记忆化)



下面是代码具体实现:

关于时间复杂度的分析不太会。记了一下结论。

通常不做任何处理,就直接杜教筛的话(分块计算+记忆化递归处理),复杂度是$O(N^{\frac{3}{4}})$

但是如果预处理出前$N^{\frac{2}{3}}$个前缀和,那么总的复杂度就可以降到$O(N^{\frac{2}{3}})$

BZOJ 3944: Sum,杜教筛入门题。

多个询问,给出N,求$\sum_{n=1}^{N}\mu(n)$和$\sum_{n=1}^{N}\phi(n)$

也就是求上面的两个例子。

这里直接给出代码,用的是预处理前$N^{\frac{2}{3}}$个前缀和+hash表进行记忆化。

复杂度$O(N^{\frac{2}{3}})$

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define DJM 1664510
//#define DJM 10
#define ll long long
using namespace std;
ll phi[DJM+50],mu[DJM+50];
struct Pii{
	int x; ll a,b;
	Pii(int _x=0,ll _a=0,ll _b=0):x(_x),a(_a),b(_b){}
}nl;
struct Hash_Table{//
	#define hmod 1425367
	int nxt[hmod],head[hmod],hnt;
	Pii info[hmod];
	Hash_Table(){hnt=2;}
	void Push(Pii rtm){
		static int u; u=rtm.x%hmod;
		info[hnt]=rtm; nxt[hnt]=head[u]; head[u]=hnt++;
	}
	Pii Find(int x){
		static int u; u=x%hmod;
		for(int i=head[u];i;i=nxt[i]) if(info[i].x==x) return info[i];
		return nl;
	}
}H;
void Sieve(){
	static bool np[DJM+50];
	static int prime[DJM+50],pnt;
	phi[1]=mu[1]=1;
	for(int i=2;i<=DJM;i++){
		if(!np[i]) prime[++pnt]=i,mu[i]=-1,phi[i]=i-1;
		for(int j=1;j<=pnt&&i<=DJM/prime[j];j++){
			np[i*prime[j]]=1;
			if(i%prime[j]){mu[i*prime[j]]=-mu[i]; phi[i*prime[j]]=phi[i]*phi[prime[j]];}
			else{phi[i*prime[j]]=phi[i]*prime[j]; break;}
		}
	}
	for(int i=2;i<=DJM;i++) mu[i]+=mu[i-1],phi[i]+=phi[i-1];
}
Pii DJ_Sieve(int x){
	if(x<=DJM) return Pii(x,mu[x],phi[x]);
	if(H.Find(x).x) return H.Find(x);
	Pii tmp,now=Pii(x,1,(1ll+x)*x/2);
	for(ll i=2,last;i<=x;i=last+1){
		last=x/(x/i); tmp=DJ_Sieve(x/i);
		now.a-=tmp.a*(last-i+1); now.b-=tmp.b*(last-i+1);
	}
	H.Push(now); return now;
}
int main(){
	Sieve();
	int Case,n; Pii ans;
	scanf("%d",&Case);
	for(int i=1;i<=Case;i++){
		scanf("%d",&n);
		if(n==0) {printf("0 0\n"); continue;}
		ans=DJ_Sieve(n);
		printf("%lld %lld\n",ans.b,ans.a);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8315251.html

时间: 2024-10-09 07:20:33

●杜教筛入门(BZOJ 3944 Sum)的相关文章

杜教筛入门

以下主要的话都用无序列表表示. 诶,是不是应该先讲背景 有什么好讲的? 问一个积性函数的前缀和,项数到1e10. 前置知识 线性筛,积性函数. 正文 钦定你已经可以再\(O(\sqrt{n})\)的复杂度内求出: \[\sum_{i=1}^n{\lfloor \frac{n}{i} \rfloor}\] \[\sum_{i=1}^n{i\times \lfloor \frac{n}{i} \rfloor}\] 对于第一个,先枚举小于\(\sqrt n\)的i,得出这段的值:又因为\(i\)在一段

bzoj 3944 Sum —— 杜教筛

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3944 杜教筛入门题! 看博客:https://www.cnblogs.com/zjp-shadow/p/8491542.html 写法模仿其他博客的,但很慢啊... 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<ma

3944: Sum(杜教筛)

3944: Sum Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 4930  Solved: 1313[Submit][Status][Discuss] Description Input 一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问 Output 一共T行,每行两个用空格分隔的数ans1,ans2 Sample Input 6 1 2 8 13 30 2333 Sample Output 1 1

【bzoj 4176】 Lucas的数论 莫比乌斯反演(杜教筛)

Description 去年的Lucas非常喜欢数论题,但是一年以后的Lucas却不那么喜欢了. 在整理以前的试题时,发现了这样一道题目“求Sigma(f(i)),其中1<=i<=N”,其中 表示i的约数个数.他现在长大了,题目也变难了. 求如下表达式的值: 一行一个整数ans,表示答案模1000000007的值. Sample Input 2 Sample Output 8 HINT 对于100%的数据n <= 10^9. 题解: 解锁新技能:杜教筛. 再复习一下: 若$F(n)=\s

数论入门——莫比乌斯函数,欧拉函数,狄利克雷卷积,线性筛,莫比乌斯反演,杜教筛

一个菜鸡对数论的一点点理解... 莫比乌斯函数 定义函数\(\mu(n)\)为: 当n有平方因子时,\(\mu(n)=0\). 当n没有平方因子时,\(\mu(n)=(-1)^{\omega(n)}\),\(\omega(n)\)表示n不同质因子的个数. 性质1: \(\sum_{d|n}\mu(d)=[n=1]\) 证明:我们把n分解质因数,则原式\(=(-1+1)^{\omega(n)}=0\). 因为对于不同的质因子,只有选和不选两种方案,这是一个组合数相加的形式,偶数加奇数减,根据二项式

bzoj 4916 神犇和蒟蒻 杜教筛

第一题结合莫比乌斯函数定义,值恒为1. 第二题,phi(i^2) = phi(i) * i,根据欧拉函数的定义式能推出来,每个质因子的指数都增加一倍,都提出来一份,就是原先的phi(i)*i.然后还是跟g(x)卷一下,杜教筛即可. 1 #include <cstdio> 2 #include <map> 3 #include <cmath> 4 using namespace std; 5 typedef long long ll; 6 const int MAXN =

洛谷P4213 Sum(杜教筛)

题目描述 给定一个正整数N(N\le2^{31}-1)N(N≤231−1) 求ans_1=\sum_{i=1}^n\phi(i),ans_2=\sum_{i=1}^n \mu(i)ans1?=∑i=1n??(i),ans2?=∑i=1n?μ(i) 输入输出格式 输入格式: 一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问 输出格式: 一共T行,每行两个用空格分隔的数ans1,ans2 输入输出样例 输入样例#1: 复制 6 1 2 8 13 30 2

P4213 【模板】杜教筛(Sum)

\(\color{#0066ff}{题 目 描 述}\) 给定一个正整数\(N(N\le2^{31}-1)\) 求 \(\begin{aligned} ans_1=\sum_{i=1}^n\varphi(i) \end{aligned}\) \(\begin{aligned} ans_2=\sum_{i=1}^n \mu(i) \end{aligned}\) \(\color{#0066ff}{输 入 格 式}\) 一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N

【模板】杜教筛(Sum)

传送门 Description 给定一个正整数\(N(N\le2^{31}-1)\) 求 \[ans1=\sum_{i=1}^n \varphi(i)\] \[ans_2=\sum_{i=1}^n \mu(i)\] Solution 总算是写了一个不会\(TLE\)的杜教筛,不想用\(map\),因此上了一个很丑的\(Hash\)-- Code #include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(