1.原理
众所周知,线性筛可以筛素数,那么,一个数可以分解成素数的次方相乘的形式,而积性函数无非就是在原有的基础上把原来的素数次方给套上了一个f(),那么筛素数的时候做到了把素数次方相乘的所有情况搞了一遍,那么在原有的基础上套上一个f()也一样,这样就变成了积性函数的结果。
对积性函数的考虑可以在原有的素数表上去添加,可以依据筛素数的表把积性函数所具有的一些个特殊情况给考虑完全。
对于复杂的积性函数,就是先把下表整出来,再在这个表上套f(),考虑特殊情况,最后再对照着写代码。线性筛的优点再于只筛一次,找准算式,算就行了,无需考虑被重复筛的情况。
下表就是线性筛筛素数的表。
若用埃氏筛法搞积性函数的话需要注意一点,埃氏筛不保证只筛一次,而重复的时候要保证计算积性函数的算式在运算的过程中不出错。
比如一个积性函数为f(x),那么再i%pime==0时,由于i与prim不互素,那么就需要特殊处理,比如把i,分离出来,f(i^k*d)= f(i^k) *f(d), 对于其他的,i与prim 互素,直接f(i) *f(prime)。 总之就是具体情况具体分析。
2DirichletDirichlet 前缀和 把复杂的降低到nloglogn.
DirichletDirichlet 前缀和
For(i,1,cnt)
{
for(reg int j=1;j*pri[i]*1ll<=n;++j) a[j*pri[i]]+=a[j];
//j*pri不变时 前面的能整除j*pri的数的对应函数的加和
}
//Dirichlet 后缀和
len——质数表长度;v[]——质数表;A[]——后缀和函数
for (int i = 0; i < len; i++)
{
for (int j = N / v[i]; j; j--)//可以理解成考虑每个质因数对后缀和的影响
{
A[j] = (A[j] + A[j * v[i]]) % MOD; // j不变时,后面的要被j整除的数的对应函数的加和
}
}
3.一些积性函数的线性筛
约数个数函数
int cnt = 0;
int t[N];
bitset<N> gg;
int prime[N];
int g[N];
void fip()
{
t[1]=1;
g[1]=1;
gg[1]=1;
for(int i=2;i<=m;i++)
{
if(gg[i]==0)
{
prime[++cnt]=i;
t[i]=2;
g[i]=1;
}
for(int j=1;j<=cnt&&prime[j]<=m/i;j++)
{
gg[i*prime[j]]=1;
if(i%prime[j]==0)
{
g[i*prime[j]]=g[i]+1;
t[i*prime[j]]=t[i]/(g[i]+1)*(g[i*prime[j]]+1);
// 这里要注意 t[i]/(g[i]+1) *(g[i*prime[j]]+1) 是对的但是 t[i] *((g[i*prime[j]]+1)/(g[i]+1))不对因为有可能除不尽
//t[i]/(g[i]+1)*(g[i]+2)
break;
}
g[i*prime[j]]=1;
t[i*prime[j]]=t[i]*t[prime[j]]; //t[i]*2
}
}
}
莫比乌斯函数
int g[N+10];
int cnt = 0;
int prime[N+10];
int mu[N+10];
void getmu()
{
mu[1] = 1;
for (int i = 2; i <= N; i++)
{
if (g[i] == 0)
{
prime[++cnt] = i;
mu[i] = -1;
}
for (int j = 1; j <= cnt && i <=N / prime[j]; j++)
{
g[i * prime[j]] = 1;
if (i % prime[j] == 0)
{
break;
}
mu[i * prime[j]] = -mu[i];
}
}
}
4 一些由函数组合而成的卷积的线性筛
这里