线性筛/欧拉筛(知识整理+板子总结)

思路来源

https://blog.csdn.net/controlbear/article/details/77527115(约数个数/约数和线性筛)

 

欧拉筛就是一类强行把筛降到线性O(n)的筛,

是线性筛的一种叭……

我对定义这种东西不大了解……

以下整理几种欧拉筛

筛素数prime、欧拉函数phi(n)、莫比乌斯函数μ(n)

并给出一定的证明过程

 

①素数筛

typedef long long ll;
bool ok[maxn];
int prime[maxn],cnt;
void sieve()
{
	for(ll i=2;i<maxn;++i)
	{
		if(!ok[i])prime[cnt++]=i;
		for(int j=0;j<cnt;++j)
		{
			if(i*prime[j]>=maxn)break;
			ok[i*prime[j]]=1;
			if(i%prime[j]==0)break; 
		}
	}
}

每次用已筛出来的质数去筛更大的数,

每个合数只被它最小的质因子筛掉,

试想,如果2*6筛了12之后还没break,

而是用3*6筛掉18,那么18还会被2*9筛一次,就重复了

而根本原因就是6有2这个因子,

而3*6筛掉的数一定也有2这个因子,

3*6这个数应该被2这个因子筛掉,而不是3

 

2020年3月31日更新:

tls的欧拉筛,其中d[i]代表i的最小素因子

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxp = 101;
int t, tot, pr[maxp], d[maxp];
int main(){
	for(int i = 2; i < maxp; ++i) {
		if(!d[i])
		    pr[tot++] = d[i] = i;
		for(int j = 0, k; (k = i * pr[j]) < maxp; ++j) {
		    d[k] = pr[j];
		    if(d[i] == pr[j])
		        break;
		}
	}
    return 0;
}

②欧拉函数筛(\varphi (n)

typedef long long ll;
bool ok[maxn];
int prime[maxn],phi[maxn],cnt;
void sieve()
{ 
        phi[1]=1;
	for(ll i=2;i<maxn;++i)
	{
		if(!ok[i])
		{
			prime[cnt++]=i;
			phi[i]=i-1;
		}
		for(int j=0;j<cnt;++j)
		{
			if(i*prime[j]>=maxn)break;
			ok[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];//prime[j]是i的因子 prime[j]的素因子项包含在i的素因子项里
				break; 
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);//prime[j]与i互质 phi[i*prime[j]=phi[i]*phi[prime[j]]
		}
	}
}

思路来源

https://blog.csdn.net/bojack_/article/details/78313778

解释

其实就是看prime[j]是否已经在i中出现过了,第一次出现要减1,后面的不减

特判n==1,phi[1]=1

如果n=p_{1}^{a_{1}}*p_{2}^{a_{2}}*...*p_{n}^{a_{n}},pi为素因子,

\varphi (n)=p_{1}^{a_{1}}*p_{2}^{a_{2}}*...*p_{n}^{a_{n}}*\tfrac{p_{1}-1}{p_{1}}*\tfrac{p_{2}-1}{p_{2}}*...*\tfrac{p_{n}-1}{p_{n}}=n*\tfrac{p_{1}-1}{p_{1}}*\tfrac{p_{2}-1}{p_{2}}*...*\tfrac{p_{n}-1}{p_{n}}

对于i*prime[j],

①i%prime[j]==0,prime[j]是素数,所以i包含prime[j]这个素因子,i是prime[j]的倍数

\varphi (i*prime[j])=i*prime[j]*\tfrac{p_{1}-1}{p_{1}}*\tfrac{p_{2}-1}{p_{2}}*...*\tfrac{p_{n}-1}{p_{n}}

由于i里面有p_{1} p_{2} ...p_{n}等素因子,故

\varphi(i)=i*\tfrac{p_{1}-1}{p_{1}}*\tfrac{p_{2}-1}{p_{2}}*...*\tfrac{p_{n}-1}{p_{n}}

所以\varphi (i*prime[j])=\varphi (i)*prime[j]

②i%prime[j]!=0,又prime[j]是素数,所以i里面没有prime[j]这个素因子,所以i和prime[j]互素

由欧拉函数的积性可知,\varphi (i*prime[j])=\varphi (i)*\varphi(prime[j])

\varphi(prime[j])=prime[j]-1

\varphi (i*prime[j])=\varphi (i)*(prime[j]-1)

③莫比乌斯函数筛(\mu (n)

typedef long long ll;
bool ok[maxn];
int prime[maxn],mu[maxn],cnt;
void sieve()
{
	mu[1]=1;
	for(ll i=2;i<maxn;++i)
	{
		if(!ok[i])
		{
			prime[cnt++]=i;
			mu[i]=-1;
		}
		for(int j=0;j<cnt;++j)
		{
			if(i*prime[j]>=maxn)break;
			ok[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				mu[i*prime[j]]=0;//如果开的是全局,就不用管 
				break; 
			}
			else mu[i*prime[j]]=-mu[i];
		}
	}
}

图片来自百度百科:莫比乌斯函数

特判n==1,mu[1]=1

对于一个素数p,μ(p)=-1

而一个数i*prime[j]被最小素因子prime[j]筛到的时候,

①i%prime[j]!=0,i里没有prime[j]这个素因子

i*prime[j]是比i多一个素因子prime[j]的,

所以μ(i*prime[j])=-μ(i)即可

②i%prime[j]==0,i里有prime[j]这个素因子

说明i*prime[j]里至少有两个prime[j]这个素因子,

根据莫比乌斯函数定义,μ(i*prime[j])=0,

开全局变量的话就不用管

 

搞个三合一板好了,以后直接粘着用……

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
bool ok[maxn];
int prime[maxn],phi[maxn],mu[maxn],cnt;
void sieve()
{
	phi[1]=mu[1]=1;
	for(ll i=2;i<maxn;++i)
	{
		if(!ok[i])
		{
			prime[cnt++]=i;
			phi[i]=i-1;
			mu[i]=-1;
		}
		for(int j=0;j<cnt;++j)
		{
			ll k=i*prime[j];
			if(k>=maxn)break;
			ok[k]=1;
			if(i%prime[j]==0)
			{
				phi[k]=phi[i]*prime[j];
				mu[k]=0;
				break; 
			}
			else
			{
			 phi[k]=phi[i]*(prime[j]-1);
			 mu[k]=-mu[i];
		    }
		}
	}
}
int main()
{
	sieve();
    return 0;
}

④积性函数线性筛

思路来源:https://www.cnblogs.com/zwfymqz/p/9337898.html

线性筛中,k只会被最小素因子prime[j]筛到,

所以,分i是否有prime[j]这个素因子,是否全为prime[j]这个素因子来讨论

约数个数线性筛、约数和线性筛都是这个思想,可参考上述链接

 

以2019南京网络赛 E.K Sum为例,这里,sieve中的f[]是一个积性函数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e7+10;
const int inv6=(mod+1)/6;
const int N=1e5+10;
bool ok[maxn];
int prime[maxn],cnt;
int low[maxn],lowp[maxn];
ll f[maxn];
int T,n,len,x;
ll res,v;
char s[N];
map<int,ll>ff;
void sieve()
{
    f[1]=1;
    for(ll i=2;i<maxn;++i)
    {
        if(!ok[i])
        {
            prime[cnt++]=i;
            low[i] = lowp[i] = i;
        	f[i] =(i*i-1)%mod;/*i为质数的情况f(p)*/ 
        }
        for(int j=0;j<cnt;++j)
        {
            ll k=i*prime[j];
            if(k>=maxn)break;
            ok[k]=1;
            if(i%prime[j]==0)//i中出现过prime[j] 
            {
            	low[k]=prime[j];
            	lowp[k]=lowp[i]*prime[j];
            	if(i==lowp[i])f[k]=f[i]*(f[prime[j]]+1)%mod;/*i中全为prime[j] f(p^k)的情况*/
            	else f[k]=f[i/lowp[i]]*f[lowp[i]*prime[j]]%mod;/*i中不全为prime[j] 将最小素因子prime[j]和其他分开 显然互质*/
                break; 
            }
            else
            {
             low[k]=lowp[k]=prime[j];
             f[k]=f[i]*f[prime[j]]%mod;//i中没出现过prime[j] i与prime[j]互质 
            }
        }
    }
    for(int i=2;i<maxn;++i)
    {
    	f[i]=(f[i]+f[i-1])%mod; 
    }
}
ll modpow(ll x,ll n,ll mod)
{
	ll res=1;
	for(;n;n/=2,x=x*x%mod)
	if(n&1)res=res*x%mod;
	return res;
}
ll cal(ll x)
{
	ll inv=modpow(x-1,mod-2,mod);
	ll y=modpow(x,v,mod);
	ll z=modpow(x,2,mod);
	return (y-z+mod)%mod*inv%mod;
}
ll getf(int n)
{
	if(n<maxn)return f[n];
	if(ff.count(n))return ff[n];
	ll ans=1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
	for(int l=2,r;l<=n;l=r+1)
	{
		r=n/(n/l);
		ans=(ans+mod-1ll*(r-l+1)*getf(n/l)%mod)%mod;
	} 
	return ff[n]=ans;
}
ll solve(int n)
{
	ll ans=0;
    for(int l=1,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        if(r==n)ans=(ans+(getf(r)-getf(l-1)+mod)%mod*(res-1+mod)%mod)%mod;
        else ans=(ans+(getf(r)-getf(l-1)+mod)%mod*cal(n/l)%mod)%mod;
    }
    return ans;
}
int main()
{
    sieve();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%s",&n,s);
        len=strlen(s);
        res=0;v=0;
        for(int i=0;i<len;++i)
        {
			res=(res*10+(s[i]-'0'))%mod;
			v=(v*10+(s[i]-'0'))%(mod-1);
		}
		v=(v+1)%(mod-1);
        printf("%lld\n",solve(n));
    }
    return 0;
} 

 

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值