线性筛合集

Posted captain1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性筛合集相关的知识,希望对你有一定的参考价值。

迟到的线性筛合集。

1.线性筛质数。
这个不讲了大家都会。不过他是下面的基础。注意每个数都是被最小质因子筛去的。

2.线性筛逆元。
这个有两种做法。第一种是质数直接用费马小定理,然后根据逆元是完全积性函数直接乘起来。
第二种直接线性递推。设(p = ki + b),则有(ki + b equiv 0 (mod p),b = p \% i,k = lfloorfrac{p}{i} floor)
(b equiv -ki (mod p))两边同乘(inv_b,inv_i),有:(inv_i equiv -k * inv_b (mod p))
得到:(inv[i] = (p - lfloorfrac{p}{i} floor) * inv[p\%i] \% p) 边界为(inv[0] = inv[1] = 1),线性递推即可。

3.线性求欧拉函数。
欧拉函数是积性函数,不是完全积性函数。
对于质数,(varphi(x) = x - 1).
对于合数,如果对于现在被筛去的数,当前最小质因子是一个新的质因子,那么(varphi(i * p[j]) = varphi(i) * varphi(p[j]))
否则(varphi(i * p[j]) = varphi(i) * p[j])

上面一项是因为两数互质,可以用积性函数的性质。
下面一项的证明:(可能很麻烦……反正是从别人那里看来的,不知道有没有更优秀的证明)
对于i,([1,i))中与i不互质的数有(i - varphi(i))个。
对于任意数n,如果其与i互质,那么n+i与i互质。如果其与i不互质,那么n+i与i不互质。前者可以用反证法,后者是显然的。
可以推论得到,对于数i * p,其中与i不互质的数个数为(i * p - p * varphi(i))
那么显然有(i * p - p * varphi(i) = i * p - varphi(i * p)),所以(p * varphi(i) = varphi(i * p))

4.线性求莫比乌斯函数。
莫比乌斯函数也是积性函数。根据其定义:
(mu(i) = 1 (i = 1))
(mu(i) = (-1)^k (i = p_1p_2…p_k))
(mu(i) = 0 (其余情况))
我们就可以得到其线性求法。对于质数,显然有(mu(i) = -1)
对于合数,如果正在被筛的数已经含有当前的最小素因子,那么说明这个正在被筛的数含有平方因子,所以(mu(i) = 0)
否则根据定义(mu(i * p[j]) = mu(i) * mu(p[j])),即(mu(i*p[j]) = -mu(i))

5.线性求约数个数和约数和。
这个用到唯一分解定理的性质。
任意一个数n可以被唯一分解为若干个质数的若干次幂的乘积,即(n = p_1^{a_1}p_2^{a_2}…p_k^{a_k})
(d(i))为约数个数,(ds(i))为约数和。
则有(d(n) = prod_{i=1}^{k}(a_i + 1)),这个很显然,就是每个质数取0~(a_k)次幂中的一个,乘法原理。
(ds(n) = (1 + p_1 + p_1^2 + … + p_1^{a_1}) * (1 + p_2 + p_2^2 + … + p_2 ^ {a_2}) * … *(1 + p_k + p_k^2 + … + p_k^{a_k})),也就是(ds(n) = prod_{i=1}^ksum_{j=0}^{a_i}p_i^j)(这个似乎有点不利于理解??)

我们先说求约数个数。这里我们需要令(num[i])表示i的最小素因子在i中的出现次数。
对于质数,有(d[i] = 2,num[i] = 1)
对于合数,那么如果这个数没有出现过,则很明显(d[i * p[j]] = d[i] * d[p[j]],num[i * p[j]] = 1)
否则这个数出现过,那么我们需要先除以最小素因子出现的次数+1,再乘以这个次数+2,并更新此值,也即是(d[i*p[j]] = d[i] / (num[i] + 1) * (num[i] + 2),num[i*p[j]] = num[i] + 1)

约数和与之是相似的。我们令(dg(i))表示i的最小素因子的等比数列的和(就先这么叫吧??)
质数显然有(ds[i] = dg[i] = i + 1)
合数的话,如果这个数以前没出现过,那么(ds[i*p[j]] = ds[i] * ds[p[j]])(可以看出这俩都是积性函数)(dg[i*p[j]] = p[j]+1)
否则和上一个一样,我们需要先除以这个等比数列,再乘上这个等比数列乘以当前质数+1.即(ds[i*p[j]] = ds[i] / dg[i] * (dg[i] * p[j] + 1),dg[i*p[j]] = dg[i] * p[j] + 1)

然后就完事啦。
(代码可能有误,未经测试)

#include<bits/stdc++.h>
#define rep(i,a,n) for(register int i = a;i <= n;i++)
#define per(i,n,a) for(register int i = n;i >= a;i--)
#define enter putchar(‘
‘)
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 10000005;
const int mod = 998244353;

int read()
{
   int ans = 0,op = 1;char ch = getchar();
   while(ch < ‘0‘ || ch > ‘9‘) {if(ch == ‘-‘) op = -1;ch = getchar();}
   while(ch >=‘0‘ && ch <= ‘9‘) ans = ans * 10 + ch - ‘0‘,ch = getchar();
   return ans * op;
}

int p[M],phi[M],mu[M],num[M],d[M],ds[M],dg[M],inv[M],tot;
bool np[M];

void euler()
{
   np[1] = 1,inv[0] = inv[1] = 1,mu[1] = 1,d[1] = 1,ds[1] = 1;
   rep(i,2,M-2)
   {
      inv[i] = (mod - mod / i) * inv[mod % i] % mod;
      if(!np[i]) p[++tot] = i,phi[i] = i-1,mu[i] = -1,d[i] = 2,num[i] = 1,ds[i] = dg[i] = i + 1;
      for(int j = 1,k = i * p[j];k <= M-2;j++)
      {
     np[k] = 1;
     if(!(i % p[j]))
     {
        phi[k] = phi[i] * p[j];
        d[k] = d[i] / (num[i] + 1) * (num[i] + 2),num[k] = num[i] + 1;
        ds[k] = ds[i] / dg[i] * (dg[i] * p[j] + 1),dg[k] = dg[i] * p[j] + 1;
     }
     phi[k] = phi[i] * phi[p[j]];
     d[k] = d[i] * d[p[j]],num[k] = 1;
     ds[k] = ds[i] * ds[p[j]],dg[k] = p[j] + 1;
      }
   }
}

int main()
{
   euler();
   return 0;
}

































以上是关于线性筛合集的主要内容,如果未能解决你的问题,请参考以下文章

筛素数算法——线性筛素数算法

线性筛素数(欧拉筛)

线性筛素数(欧拉筛)

线性筛素数(欧拉筛)

线性筛素数(欧拉筛)

算法杂谈线性筛