线性筛

- 用欧拉筛法线性筛素数

        \ \ \ \ \ \,      我们知道,若 x x x为素数的话,那么必然若有任意一个不为 1 1 1的数 a a a ( a ⋅ x ) (a\cdot x) (ax)不是素数,打上标记。

      &ThinSpace; \ \ \ \ \ \,      并且必然有 a &lt; a ⋅ x a&lt;a\cdot x a<ax x &lt; a ⋅ x x&lt;a\cdot x x<ax,所以我们不断枚举 a a a,在之前若是没有被打上标记,那么 a a a就是素数。再不断枚举之前筛出来的素数 x x x,对范围 [ a , n ] [a,n] [a,n]中的 ( a ⋅ x ) (a\cdot x) (ax)打上标记。

      &ThinSpace; \ \ \ \ \ \,      若是当前 a a a已经是某一个 x x x的倍数,那么我们便可不必向后枚举 x x x了,因为后面的 ( a ⋅ x ) (a\cdot x) (ax)一定被打过标记了,这样子可以保证每一个数最多被打过一次标记,复杂度严格 O ( n ) O(n) O(n)

//prime[0]为素数个数。
bool used[N];
long long prime[N];
long long n,q;
void Get_Prime(long long n){
	used[1]=1;
	for(long long i=2;i<=n;i++){
		if(!used[i])prime[++prime[0]]=i;
		for(int j=1;j<=prime[0]&&i*prime[j]<=n;j++){
			used[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		} 
	}
}

- 用欧拉筛法线性筛欧拉函数 φ \varphi φ,莫比乌斯函数 μ \mu μ

  • 筛欧拉函数 φ \varphi φ

      &ThinSpace; \ \ \ \ \ \,      根据欧拉函数 φ \varphi φ的3条性质:

      &ThinSpace; 1 \ \ \ \ \ \,1      1.若 x x x为素数, φ ( x ) = x − 1 \varphi(x)=x-1 φ(x)=x1;

      &ThinSpace; 2 \ \ \ \ \ \,2      2.若 x % p = 0 x\%p=0 x%p=0 φ ( x ⋅ p ) = φ ( x ) ⋅ p \varphi(x\cdot p)=\varphi(x)\cdot p φ(xp)=φ(x)p;

      &ThinSpace; 3 \ \ \ \ \ \,3      3.若 x % p ≠ 0 x\%p\neq 0 x%p̸=0,且 p p p为素数, φ ( x ⋅ p ) = φ ( x ) ⋅ ( p − 1 ) \varphi(x\cdot p)=\varphi(x)\cdot (p-1) φ(xp)=φ(x)(p1);

      &ThinSpace; \ \ \ \ \ \,      稍微改动一下线性筛素数就好了。

bool vis[N];
int prim[N],phi[N];
void Get_Phi(int n){
  phi[1]=1;
  for(int i=2;i<=n;i++){
    if(!vis[i]){phi[i]=i-1;prim[++prim[0]]=i;}
    for(int j=1;j<=prim[0]&&i*prim[j]<=n;j++){
      vis[i*prim[j]]=1;
      if(i%prim[j]==0){phi[i*prim[j]]=phi[i]*prim[j];break;}
      phi[i*prim[j]]=phi[i]*(prim[j]-1);
    }
  }
}
  • 筛莫比乌斯函数 μ \mu μ

      &ThinSpace; \ \ \ \ \ \,      根据性质,若 x x x为素数, μ ( x ) = − 1 \mu(x)=-1 μ(x)=1;对于任意数 a a a,存在 μ ( a ⋅ x ) = − μ ( a ) \mu(a\cdot x)=-\mu(a) μ(ax)=μ(a)

      &ThinSpace; \ \ \ \ \ \,      再稍微改动一下线性筛素数就好了。

bool vis[N];
int prim[N],mu[N];
void Get_Mu(int n){
  mu[1]=1;
  for(int i=2;i<=n;i++){
    if(!vis[i]){mu[i]=-1;prim[++prim[0]]=i;}
    for(int j=1;j<=prim[0]&&i*prim[j]<=n;j++){
      vis[i*prim[j]]=1;
      if(i%prim[j]==0)break;
      mu[i*prim[j]]=-mu[i];
    }
  }
}

- 对于任意积性函数 f f f线性筛

      &ThinSpace; \ \ \ \ \ \,      对于任意积性函数 f f f,我们如何线性筛。

  • 如果 p p p是素数: f ( p ) = F 1 ( p ) f(p)=F_1(p) f(p)=F1(p)

  • 如果 p p p是素数, i % p ≠ 0 i\%p\neq0 i%p̸=0 f ( p i ) = f ( i ) × F 2 ( p ) f(pi)=f(i)\times F_2(p) f(pi)=f(i)×F2(p)

  • 如果 p p p是素数, i % p = 0 i\%p=0 i%p=0

    我们把 p i pi pi 分解成 p c x p^cx pcx ,可以保证 p c p^c pc x x x互质,那么有: f ( p i ) = f ( p c x ) = f ( x ) ∗ F 3 ( p , c ) f(pi)=f(p^cx)=f(x)*F_3(p,c) f(pi)=f(pcx)=f(x)F3(p,c)

      &ThinSpace; \ \ \ \ \ \,      其中,函数 F 1 F_1 F1, F 2 F_2 F2, F 3 F_3 F3需要情况制定,模板如下:

bool vis[N];
int pri[N],cnt[N],power[N];
int prime[N],f[N];
void Get_Shai(int n){
	f[1]=1;
	for(long long i=1;i<=n;i++)power[i]=1;
  for(int i=2;i<=n;i++){
    if(!vis[i]){
			cnt[i]=1;pri[i]=i;power[i]=i;
			prime[++prime[0]]=i;
			f[i]=F1(i);
		}
    for(int j=1,v,pc;j<=prime[0]&&i*prime[j]<=n;j++){
    	v=i*prime[j];
      vis[v]=1;
      if(i%prime[j]==0){
				cnt[v]=cnt[i]+1;pri[v]=pri[i];power[v]=power[i]*pri[i];
				f[v]=f[v/power[v]]*F3(pri[v],cnt[v]);
				break;
			}
			cnt[v]=1;pri[v]=prime[j];power[v]=prime[j];
      f[v]=f[i]*F2(prime[j]);
    }
  }
}

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<set>
using namespace std;
const int inf=0x7fffffff;
const double eps=1e-10;
const double pi=acos(-1.0);
//char buf[1<<15],*S=buf,*T=buf;
//char getch(){return S==T&&(T=(S=buf)+fread(buf,1,1<<15,stdin),S==T)?0:*S++;}
inline int read(){
	int x=0,f=1;char ch;ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=0;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch&15);ch=getchar();}
	if(f)return x;else return -x;
}
const int N=1e7+10;
int Sig(int a){
	int ret=0;
	for(int i=1;i<=a;i++)if(a%i==0)ret++;
	return ret;
}
bool vis[N];
int pri[N],cnt[N],power[N];
int phi[N],mu[N],prime[N],sigma0[N],sigma1[N],inv[N];
void Get_Shai(int n){
	phi[1]=mu[1]=sigma0[1]=sigma1[1]=inv[1]=power[1]=1;
	for(long long i=2;i<=n;i++)
  inv[i]=(n-n/i)*inv[n%i]%n,power[i]=1;
  
  for(int i=2;i<=n;i++){
    if(!vis[i]){
			cnt[i]=1;pri[i]=i;power[i]=i;
			
			prime[++prime[0]]=i;
			phi[i]=i-1;
			mu[i]=-1;
			sigma0[i]=2;
			sigma1[i]=i+1;
		}
    for(int j=1,v,pc;j<=prime[0]&&i*prime[j]<=n;j++){
    	v=i*prime[j];
      vis[v]=1;
      if(i%prime[j]==0){
				cnt[v]=cnt[i]+1;pri[v]=pri[i];power[v]=power[i]*pri[i];
				
				phi[v]   =phi[i]*prime[j];
				sigma0[v]=sigma0[v/power[v]]*(cnt[v]+1);
				sigma1[v]=sigma1[v/power[v]]*(power[v]*pri[v]-1)/(pri[v]-1);
				break;
			}
			cnt[v]=1;pri[v]=prime[j];power[v]=prime[j];
			
			phi[v]   =phi[i]*(prime[j]-1);
			mu[v]    =-mu[i];
      sigma0[v]=sigma0[i]*2;
      sigma1[v]=sigma1[i]*(prime[j]+1);
    }
  }
}
int main()
{
	int n=read();
	Get_Shai(n);
	for(int i=1;i<=n;i++)printf("%d ",phi[i]);printf("\n");
	for(int i=1;i<=n;i++)printf("%d ",mu[i]);printf("\n");
	printf("%d ",prime[0]);for(int i=1;i<=prime[0];i++)printf("%d ",prime[i]);printf("\n");
	for(int i=1;i<=n;i++)printf("%d ",sigma0[i]);printf("\n");
	for(int i=1;i<=n;i++)printf("%d ",sigma1[i]);printf("\n");
	for(int i=1;i<=n;i++)printf("%d ",inv[i]);printf("\n");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值