读贾志鹏线性筛有感

先拜大牛。感谢贾志鹏严谨的思维。以及简单清晰的论文描述。

一定要结合论文看。我只是提出我觉得关键的部分。论文在网上随处可见。贾志鹏线性筛。

开头两种线性筛的比较。

一种是传统的线性筛。时间复杂度为N*log(log(N))。

另外一种是优化了合数的筛法。文中称作Euler线性筛。

其优化的地方。

举个例子:合数6。 是2的倍数也是3的倍数。 当你用传统的筛法的时候在遍历2的倍数的时候会遍历到6。遍历3的倍数的时候同样也会遍历到6。

而另外一种只会筛出6为2的倍数。3就不会筛6了。

另外个人认为筛法二有一个很重要的思想。当i为合数的时候。其实脑海里不认为是合数。而是素数的乘积。这样就能比较直观地确定这个算法的正确性了。

积性函数。

分为完全积性和条件积性。

我们最喜欢的积性。大概就是互素积性了。因为满足互素积性的话。根据算术基本定理。就能够简单做到推广到任意实数。

f(1) = 1 。 这个在我们高中数学题。抽象函数。就已经能简单知道了。

欧拉函数。就不再谈了。包括其线性筛的那一步至关重要的证明。也在我其它博文提到过了。

其 欧拉定理和费马小定理的作用。我得再多补充一点。

以及约数和。 n的约数和为 n*φ(n)/2.

莫比乌斯函数和容斥定理的关系。

可以发现莫比乌斯函数其实就是容斥定理的映射一般。

莫比乌斯函数 是我们再熟悉不过的了。

首先看 (-1)^r m = p1p2p3p4p5pr 其实就是在模拟容斥定理。

假如一但不是素数。那就为0.

两个函数的线性筛。这其实是我们处理问题的基本。

问题一:求1~N对质数P的乘法逆元。

关于f(n)为完全积性函数。根据同余定理可以简单获得。要证明的话。减法证同余即可。

P = nt + k

n‘  ≡ n*(t^2)*f(k)^2 (mod P)

这个证明过程很漂亮(很佩服这么顺畅,思维这么清晰)。也是根据同余定理。还有逆元的性质。就能推理的。

这个问题的意义。可以求N!的 mod P 的逆元了。逆元还是很有用的。因为毕竟除法并没有特别好的同余式。(依稀还记得那两个。)

问题二:给T组N,M.依次求出的值.(N,M<=10^6,T<=10^3)

求解gcd(a,b).把gcd(a,b)当做n.再通过欧拉函数和式。推导过程如下。

第二个等式是用d来看待式子的方法来化简和式的。

之后再穷举d即可。论文上有个优化在这里没体现。

#include<stdio.h>
#include<string.h>
#define    N 100

bool mark[N+5];
int prime[N+5];
int num;
int euler[N+5];
int Min(int a,int b){return a>b?a:b;}
void Euler()
{
    int i,j;
    num = 0;
    memset(euler,0,sizeof(euler));
    memset(prime,0,sizeof(prime));
    memset(mark,0,sizeof(mark));
    euler[1] = 1; // multiply function
    for(i=2;i<=N;i++)
    {
        if(!mark[i])
        {
            prime[num++] = i;
            euler[i] = i-1;
        }
        for(j=0;j<num;j++)
        {
            if(i*prime[j]>N){break;}
            mark[i*prime[j]] = 1;
            if(i%prime[j]==0)
            {
                euler[i*prime[j]] = euler[i]*prime[j];
                break;
            }
            else
            {
                euler[i*prime[j]] = euler[prime[j]]*euler[i];
            }
        }
    }
}

int main()
{
    int i;
    int M1,M2;
    Euler();
    for(i=0;i<num;i++)printf("%d ",prime[i]);
    printf("\n");
    for(i=1;i<=N;i++)printf("%d ",euler[i]);
    printf("\n");
    M1 = 2;
    M2 = 3;
    int sum = 0;
    int min = Min(M1,M2);
    for(i=1;i<=min;i++)
    {
        sum += euler[i]*(M1/i)*(M2/i);
    }
    printf("%d\n",sum);
}

the second problem test

问题三:给T组N,M.依次求出的值.(N,M<=10^6,T<=10^3)

在证明之前,先证明以下式子。

问题的解决推导。

第一个等式。lcm(a,b) = ab/gcd(a,b).

第二个等式。令d=gcd(a,b)。

第三个等式。转化为d的视角。(这个手法经常有)。

第四个等式。转化为莫比乌斯函数。

第五个等式。利用上述的等式来转化。注意d和d‘

第六个等式。论文中提到的有趣的化简性质。

第七个等式。至今未明白。

之后就是如论文中介绍的。g(d) 为积性函数。线性筛之。

总体上算法还是N的。

问题四:给T组N,M.依次求出的值.(N,M<=10^6,T<=10^3)

实质上就是求 其中x和y互素。的对数。

我们是时候需要有和式化成的思想了。[gcd(a,b)=1]真是漂亮的莫比乌斯函数的和式的结果。

#include<stdio.h>
#include<string.h>
#define    N 100

bool mark[N+5];
int prime[N+5];
int num;
int mobi[N+5];
int Min(int a,int b){return a>b?a:b;}
void Mobi()
{
    int i,j;
    num = 0;
    memset(mobi,0,sizeof(mobi));
    memset(prime,0,sizeof(prime));
    memset(mark,0,sizeof(mark));
    mobi[1] = 1; // multiply function
    for(i=2;i<=N;i++)
    {
        if(!mark[i])
        {
            prime[num++] = i;
            mobi[i] = -1;
        }
        for(j=0;j<num;j++)
        {
            if(i*prime[j]>N){break;}
            mark[i*prime[j]] = 1;
            if(i%prime[j]==0)
            {
                mobi[i*prime[j]] = 0;
                break;
            }
            else
            {
                mobi[i*prime[j]] = mobi[prime[j]]*mobi[i];
            }
        }
    }
}

int main()
{
    int i;
    int M1,M2;
    Mobi();
    for(i=0;i<num;i++)printf("%d ",prime[i]);
    printf("\n");
    for(i=1;i<=N;i++)printf("%d ",mobi[i]);
    printf("\n");
    M1 = 2;
    M2 = 3;
    int sum = 0;
    int min = Min(M1,M2);
    for(i=1;i<=min;i++)
    {
        sum += mobi[i]*(M1/i)*(M2/i);
    }
    printf("%d\n",sum);
}

Test problem forth

问题五:给T组N.依次求出的值.(N<=10^6,T<=10^3)

 

其实根据问题三.可以直接获得该化简出来的式子的。

然后解法和问题三一样。

但是论文上寻找积性f(n)直接筛出答案。

首先佩服一下其思维的紧密。一个变量啊。就寻找积性函数。这个转化真是清晰而又巧。

画个图就能知道 -n 是用来去重复的统计的。

.

f(n)是积性的。具体证明如论文上解释。

第一个等式:d = gcd(n,i)

第二个等式:k = i/d.且全部都除以d.gcd(a,b)=d转化成求互素(gcd(a,b)=1)的问题。

第三个等式:令d=n/d。是对应的。  其实在第二个等式就能看出是欧拉函数求约数和问题了。

第四个等式:不解释了吧。

第五个等式:手算一下容易得。

时间: 2024-08-13 10:25:36

读贾志鹏线性筛有感的相关文章

读贾志鹏《线性筛法与积性函数》笔记

1.欧拉筛法在线性时间内求素数以及欧拉函数 代码: 1 procedure get; 2 var i,j,k:longint; 3 begin 4 tot:=0; 5 fillchar(check,sizeof(check),false); 6 for i:=2 to n do 7 begin 8 if not(check[i]) then 9 begin 10 inc(tot); 11 p[tot]:=i; 12 fai[i]:=i-1; 13 end; 14 for j:=1 to tot

【2011集训贾志鹏】Crash 的数字表格

题面 题目分析 (默认\(n<m\)) 题目要求\(\sum\limits_{i=1}^n\sum\limits_{j=1}^mlcm(i,j)\). 由\(lcm(i,j)=\frac{i*j}{gcd(i,j)}\) 得: \[ \begin{split} ans & =\sum\limits_{i=1}^n\sum\limits_{j=1}^m\frac{i*j}{gcd(i,j)} \& =\sum\limits_{d=1}^n\sum\limits_{i=1}^n\sum\

bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛

对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b). Input 第一行一个数T,表示询问数. 接下来T行,每行两个数a,b,表示一个询问. Output 对于每一个询问,输出一行一个非负整数作为回答. Sample Input 4 7558588 9653114 6514903 445

BZOJ 2693: jzptab [莫比乌斯反演 线性筛]

2693: jzptab Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1194  Solved: 455[Submit][Status][Discuss] Description Input 一个正整数T表示数据组数 接下来T行 每行两个正整数 表示N.M Output T行 每行一个整数 表示第i组数据的结果 Sample Input 1 4 5 Sample Output 122 HINT T <= 10000 N, M<=1000000

读《活出生命的意义》有感

为自己的生命负责 ——读<活出生命的意义>有感 与之前老师推荐的两本书不同的是,这次阅读的这本书并不是什么有关专业的书籍,读来有趣的同时让我有了巨大的感触,学到的东西也颇多. 书籍前半部分叙述了作者在集中营受到的种种残酷.没有人性的待遇,这也是最让我震惊的一部分.如此惨绝人寰的待遇让我震惊,作者顽强的生存下来并开创意义治疗法更是让我震惊.亲人天人永隔,自己衣不遮体.食不果腹,甚至不知道自己什么时候就会死去,但作者不仅越过了这炼狱般的痛苦,更将自己的经验与学术相结合,开创意义治疗法,替人们找到绝

jzp线性筛及其简单应用

前言: 很久以前看过了线性筛,没怎么注意原理,但是后来发现线性筛还有很有用的.. 比如上次做的一道题就需要找出每个数的最小质因子,先筛再找就太慢了..一看线性筛发现就可以直接在筛的过程中处理出来了! 今天又学习了屌炸天的jzp线性筛,可以在o(n)的时间内求出欧拉函数, 莫比乌斯函数等积性函数 原理: 首先jzp线性筛并不是一种新的线性筛..其实就是jzp大牛对线性筛的一些开发应用 先回忆一下积性函数的定义 若a,b互质 则f(ab)=f(a)*f(b)的函数f 定义为积性函数,不要求a,b互质

* SPOJ PGCD Primes in GCD Table (需要自己推线性筛函数,好题)

题目大意: 给定n,m,求有多少组(a,b) 0<a<=n , 0<b<=m , 使得gcd(a,b)= p , p是一个素数 这里本来利用枚举一个个素数,然后利用莫比乌斯反演可以很方便得到答案,但是数据量过大,完全水不过去 题目分析过程(从别人地方抄来的) ans = sigma(p, sigma(d, μ(d) * (n/pd) * (m/pd))) Let s = pd, then ans = sigma(s, sigma(p, μ(s/p) * (n/s) * (m/s))

[原博客] 关于线性筛

埃氏筛法:从2开始,找到第一个没有被筛的数,把它标记为素数,然后把它的2倍.3倍……筛掉.复杂度O(nlogn). 改进的埃氏筛法:从2开始,找到第一个没有被筛的数x,把它标记为素数,然后把它的x倍.x+1倍……筛掉.复杂度O(nloglogn). 线性筛:保证每个数都被它的最小素因子筛掉.复杂度O(n). C++写起来大概是这样的: int mindiv[10000005],tot,prime[10000050]; int main(){ for(int i=2;i<=10000000;i++

[BZOJ2818] Gcd (数论,欧拉函数,线性筛)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2818 必须用线性筛. 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int maxn = 10001001; 5 LL phi[maxn], sum[maxn], n; 6 bool isprime[maxn]; 7 LL prime[maxn]; 8 int tot;