jzp线性筛及其简单应用

前言:

很久以前看过了线性筛,没怎么注意原理,但是后来发现线性筛还有很有用的。。

比如上次做的一道题就需要找出每个数的最小质因子,先筛再找就太慢了。。一看线性筛发现就可以直接在筛的过程中处理出来了!

今天又学习了屌炸天的jzp线性筛,可以在o(n)的时间内求出欧拉函数, 莫比乌斯函数等积性函数

原理:

首先jzp线性筛并不是一种新的线性筛。。其实就是jzp大牛对线性筛的一些开发应用

先回忆一下积性函数的定义 若a,b互质 则f(ab)=f(a)*f(b)的函数f 定义为积性函数,不要求a,b互质也满足的称为完全积性函数

欧拉函数和莫比乌斯函数都是积性函数但不是完全积性函数

假如我们要求 欧拉函数f(n)和莫比乌斯函数 mb(n)

显然如果n的所有质因数(p1,p2...)的次数都是1,显然p1,p2....是互质的,满足积性函数定义,则f(n)=f(p1)*f(p2).....同理mb(n)

而如果某个质因数p的次数不为1,假设为k,我们可以看(yy)出 f(p^k)=p^k-p^(k-1)=(p-1)*p^k,同时由mobius函数定义知如果某个质因数次数大于1次,则其函数值为0

那么如何在线性筛中找到次数不为1的质因子呢

我们观察 if(i%prime[j]==0) break; 这句代码,此处要筛的数n =i*prime[j],而当i%prime[j]==0 时 显然n%(prime[j]*prime[j])==0。

因此可以知道此时在n的质因子中 prime[j]的次数已经大于1了,就可以处理相应的欧拉函数和莫比乌斯函数了!

简单应用:

hdu1695

题意:
求[1,n]和[1,m]之间有多少个互质的数

做法:
以前是用容斥做的,但是容斥需要找质因数,再二进制枚举,比较慢

莫比乌斯函数其实就是容斥的系数,所以直接枚举可能出现的约数(其实就是1~n)用莫比乌斯函数求和即可

最后的式子(不判重)为sum(i=1 to n , mb(i)*(n/i)*(m/i));

这里还有一个小优化,由于是整数除法,对于i=[a,n/(n/a)]   n/i都是是一样的 ,比如 100/(21,22...25)都等于4,这样可以提前对莫比乌斯函数求前缀和,直接累加即可

具体实现见代码,大神们证明了这个优化可以把复杂度降到sqrt(n)级别。具体实现起来的确是快多了,hdu直接0ms AC了!

代码:

#include <iostream>
#include <stdio.h>
#include<string.h>
#include<algorithm>
#include<string>
#include<ctype.h>
using namespace std;
#define maxn 100000
bool notprime[maxn+10];
int prime[maxn+10];
int mb[maxn+10];
int f[maxn+10];
long long sum[maxn+10];
int np;
long long n,m;
void jzp()
{
    np=0;
    memset(notprime,0,sizeof(notprime));
    mb[1]=1;
    for(int i=2; i<=maxn; i++)
    {
        if(!notprime[i])
        {
            prime[np++]=i;
            mb[i]=-1;
            //f[i]=i-1;
        }
        for(int j=0; j<np&&i*prime[j]<=maxn; j++)
        {
            notprime[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mb[i*prime[j]]=0;
                //f[i*prime[j]]=f[i]*prime[j];
                break;
            }
            else
            {
                mb[i*prime[j]]=-mb[i];
                //f[i*prime[j]]=f[i]*(prime[j]-1);
            }
        }
    }
}
int main()
{
    int t,cas=1;
    scanf("%d",&t);
    jzp();
    sum[0]=0;
    for(int i=1;i<=maxn;i++)
    {
        sum[i]=sum[i-1]+mb[i];
    }
    while(t--)
    {
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",cas++);
            continue;
        }
        n=min(b/k,d/k);
        m=max(b/k,d/k);
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            int j=n/(n/i);
            ans+=(n/i)*(n/i)*(sum[j]-sum[i-1]);
            i=j;
        }
        ans=-(ans/2);
        for(int i=1;i<=n;i++)
        {
            int j=min(m/(m/i),n/(n/i));
            ans+=(n/i)*(m/i)*(sum[j]-sum[i-1]);
            i=j;
        }
        printf("Case %d: %I64d\n",cas++,ans);
    }
    return 0;
}

最后贴jzp筛模板

bool notprime[maxn+10];int prime[maxn+10];
int mb[maxn+10];   //mobius
int f[maxn+10];      //euler
int np;
void jzp()
{
    np=0;
    memset(notprime,0,sizeof(notprime));
    mb[1]=1;
    for(int i=2;i<=maxn;i++)
    {
        if(!notprime[i])
        {
            prime[np++]=i;
            mb[i]=-1;
            f[i]=i-1;
        }
        for(int j=0;j<np&&i*prime[j]<=maxn;j++)
        {
            notprime[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                mb[i*prime[j]]=0;
                f[i*prime[j]]=f[i]*prime[j];
                break;
            }
            else
            {
                mb[i*prime[j]]=-mb[i];
                f[i*prime[j]]=f[i]*(prime[j]-1);
            }
        }
    }
}
时间: 2024-10-07 06:17:47

jzp线性筛及其简单应用的相关文章

【数论线性筛】洛谷P1865 A%B problem

题目背景 题目名称是吸引你点进来的 实际上该题还是很水的 题目描述 区间质数个数 输入输出格式 输入格式: 一行两个整数 询问次数n,范围m 接下来n行,每行两个整数 l,r 表示区间 输出格式: 对于每次询问输出个数 t,如l或r?[1,m]输出 Crossing the line 输入输出样例 输入样例#1: 2 5 1 3 2 6 输出样例#1: 2 Crossing the line 说明 [数据范围和约定] 对于20%的数据 1<=n<=10 1<=m<=10 对于100

睡前数学一小时之线性筛素数:

睡前数学一小时之线性筛素数:1,朴素的筛素数算法:埃拉托斯特尼筛法.这是个简单再简单不过的一个素数的筛法.只是名字很拉风.这就告诉我们,往往东西不好这没什么,名字很拉风.别人也不会记住.hhhhh.这个的思路就是.每一个数都是由一个质数与和数(质数也可以)的积组成.这也是质数与和数的定义.而这个它这个筛发,就是当遇到一个质数的时候开始枚举,枚举[1,n]中间关于这个质数的倍数.每次都枚举,每次都将算出的这个数打上标记.而最后整个区间内的质数枚举完后,整个区间内的质数也就筛选出来了.这个很简单.时

读贾志鹏线性筛有感

先拜大牛.感谢贾志鹏严谨的思维.以及简单清晰的论文描述. 一定要结合论文看.我只是提出我觉得关键的部分.论文在网上随处可见.贾志鹏线性筛. 开头两种线性筛的比较. 一种是传统的线性筛.时间复杂度为N*log(log(N)). 另外一种是优化了合数的筛法.文中称作Euler线性筛. 其优化的地方. 举个例子:合数6. 是2的倍数也是3的倍数. 当你用传统的筛法的时候在遍历2的倍数的时候会遍历到6.遍历3的倍数的时候同样也会遍历到6. 而另外一种只会筛出6为2的倍数.3就不会筛6了. 另外个人认为筛

【算法学习】线性筛素数

声明:本文所涉及部分内容可能并非原创.如发现本文侵犯了您的权益,可申请博主删除. 嘛……好久没有写博客了,今天无聊来写一篇~ 为什么要写这个呢?因为这个算法让我知道了我什么都不会啊…… 闲话少说,切入正题. 1. 筛素数 素数即质数,定义就不说啦 那么我们经典的筛素数的方法呢都很简单,开一个bool数组,从1到$\sqrt n$,如果是素数就把这个数的倍数给筛掉. 时间复杂度显然:$O(nlog_n)$ 代码也很简单: void prime(int n) { for(int i = 2; i *

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

一个菜鸡对数论的一点点理解... 莫比乌斯函数 定义函数\(\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 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

* 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++