Min_25筛学习笔记

Preface
  • 模拟赛遇到了,于是学习了一下。

  • 问题一般是求积性函数 f ( x ) f(x) f(x)的前缀和,如 ∑ i = 1 n F ( i ) \sum_{i=1}^nF(i) i=1nF(i)

Solution
  • P P P表示素数集合,记 P i P_i Pi表示第 i i i个质数,其中 ∣ P ∣ = n |P|=\sqrt{n} P=n

  • 则此类问题 F ( i ) F(i) F(i)需要满足:

{ f ( p ) 是 一 个 关 于 p 的 简 单 多 项 式      p ∈ P f ( p c ) 一 般 可 以 快 速 计 算 出              o t h e r w i s e \begin{cases} f(p)是一个关于p的简单多项式\ \ \ \ p\in P\\ f(p^c)一般可以快速计算出\ \ \ \ \ \ \ \ \ \ \ \ otherwise\end{cases} {f(p)p    pPf(pc)            otherwise

  • 考虑记 S ( n , j ) = ∑ i = 2 n F ( i ) [ i 的 最 小 质 因 子 大 于 等 于 P j ] S(n,j)=\sum_{i=2}^nF(i)[i的最小质因子大于等于P_j] S(n,j)=i=2nF(i)[iPj]

  • Min25筛会把 F i F_i Fi拆成 ∑ a i i k \sum a_ii^k aiik的形式,对于每个 k k k分别筛后最后合并到一起。

递归版本
  • 那么先考虑质数的贡献,即 i i i为质数,且保证括号内条件的情况下的贡献。

  • 若记 m i n ( p ) min(p) min(p)表示 i i i的最小质因子,则再记 g ( n , j ) = ∑ i = 1 n [ i ∈ P , m i n ( p ) > P j ] F ( i ) g(n,j)=\sum_{i=1}^n[i\in P, min(p)\gt P_j]F(i) g(n,j)=i=1n[iP,min(p)>Pj]F(i)

  • 由定义,不难推出 g ( n , j ) = { g ( n , j − 1 ) P j 2 > n g ( n , j − 1 ) − F ( P j ) [ g ( n P j , j − 1 ) − ∑ i = 1 j − 1 F ( P i ) ] P j 2 ≤ n g(n,j)=\begin{cases} g(n,j-1)&{P_j}^2\gt n\\ g(n,j-1)-F(P_j)[g(\frac{n}{P_j},j-1)-\sum_{i=1}^{j-1}F(P_i)]&{P_j}^2\le n\end{cases} g(n,j)={g(n,j1)g(n,j1)F(Pj)[g(Pjn,j1)i=1j1F(Pi)]Pj2>nPj2n

  • 现在还有一个问题是 g ( n , 0 ) g(n,0) g(n,0)如何求出来。

  • 事实上,我们观察这整一个递推过程,实际上很类似埃氏筛,每一次都用一个 P j P_j Pj去把一些合数的贡献给筛去,最终筛到 n \sqrt{n} n 时,保证了还没有被质数筛去的数在 n n n范围内一定不是合数。

  • 那么继续考虑埃氏筛,实际上一开始我们是把所有数都看成质数的。那么 g ( n , 0 ) g(n,0) g(n,0)的初值也就是这个 ∑ i = 1 n i k \sum_{i=1}^ni^k i=1nik,例如当 k = 2 k=2 k=2时, g ( n , 0 ) = n ( n + 1 ) ( 2 n + 1 ) 6 g(n,0)=\frac{n(n+1)(2n+1)}{6} g(n,0)=6n(n+1)(2n+1).

  • 现在回到原问题 ∑ i = 1 n F ( i ) \sum_{i=1}^nF(i) i=1nF(i)

  • 我们要求质数的贡献,实质上就是 g ( n , ∣ P ∣ ) − g ( P j − 1 , j − 1 ) g(n,|P|)-g(P_{j-1},j-1) g(n,P)g(Pj1,j1)

  • 那么可以推出 S ( n , j ) = g ( n , ∣ P ∣ ) − ∑ i = 1 j − 1 f ( P i ) + ∑ k ≥ j ∣ P ∣ ∑ e ( f ( P k e ) S ( n P k e , k + 1 ) + f ( P k e + 1 ) ) S(n,j)=g(n,|P|)-\sum_{i=1}^{j-1}f(P_i)+\sum_{k\ge j}^{|P|}\sum_{e}(f({P_k}^e)S(\frac{n}{{P_k}^e},k+1)+f({P_k}^{e+1})) S(n,j)=g(n,P)i=1j1f(Pi)+kjPe(f(Pke)S(Pken,k+1)+f(Pke+1))

  • 事实上,后面那一部分式子就是合数的贡献,枚举质数 k k k,次数 e e e,然后与求 g g g的思想是完全类似的,注意要加上一个 f ( P k e + 1 ) f({P_k}^{e+1}) f(Pke+1)就好了.

递推版本
  • 实际上递推版本比递归更慢。因为它求的是 n \sqrt{n} n n i \frac{n}{i} in的前缀和。

  • 具体细节与递归版本类似。我们直接设 S ′ ( n , i ) = ∑ i = 2 n F ( i ) [ i 的 最 小 质 因 子 不 小 于 p i 或 i 是 质 数 ] S'(n,i)=\sum_{i=2}^nF(i)[i的最小质因子不小于p_i或i是质数] S(n,i)=i=2nF(i)[ipii].

  • 可以得到: S ′ ( n , i ) = { S ′ ( n , i + 1 ) + ∑ e ≥ 1 且   p i e + 1   ≤ n    F ( p i e ) [ S ( ⌊ n p i e ⌋ , i + 1 ) − g ( p i , i ) ] + F ( p i e + 1 ) p i 2 ≤ n S ′ ( n , i + 1 ) p i 2 ≥ n S'(n,i)=\left\{ \begin{array}{} S'(n,i+1)+\sum_{e\geq 1且\ p_i^{e+1}\ \leq n}\ \ F(p_i^e)[S(\lfloor\frac{n}{p_i^e}\rfloor,i+1)-g(p_i,i)]+F(p_i^{e+1})& &{p_i^2\leq n} \\ S'(n,i+1)& &{p_i^2\geq n} \end{array}\right. S(n,i)={S(n,i+1)+e1 pie+1 n  F(pie)[S(pien,i+1)g(pi,i)]+F(pie+1)S(n,i+1)pi2npi2n

  • 初值 S ′ ( n , ∣ P ∣ + 1 ) = g ( n , ∣ P ∣ ) S'(n,|P|+1)=g(n,|P |) S(n,P+1)=g(n,P)

Some Problems
T1

LOJ#6235. 区间素数个数

事实上就是求 1 ∼ n 1\sim n 1n素数,这个可以直接通过求的 g g g来得到,连 s s s都不用求。

注意实现的时候只需要保存 n i \frac{n}{i} in n \sqrt{n} n 个值即可。

#include <bits/stdc++.h>

#define F(i, a, b) for (int i = a; i <= b; i ++)

const int N = 700000;

using namespace std;

long long n, m, id1[N], id2[N], w[N], g[N], z[N], sqr;
bool bz[N];

int main() {
	scanf("%lld", &n), sqr = int(sqrt(n));

	F(i, 2, sqr) {
		if (!bz[i])
			z[++ z[0]] = i, bz[i] = 1;
		F(j, 1, z[0]) {
			if (z[j] * i > sqr) break;
			bz[z[j] * i] = 1;
			if (i % z[j] == 0) break;
		}
	}

	for (long long i = 1, j; i <= n; i = j + 1) {
		j = n / (n / i); w[++ m] = n / i;
		if (w[m] <= sqr) id1[w[m]] = m; else id2[n / w[m]] = m;
		g[m] = w[m] - 1;
	}
	F(j, 1, z[0])
		for (int i = 1; i <= m && z[j] * z[j] <= w[i]; i ++) {
			int k = (w[i] / z[j] <= sqr) ? id1[w[i] / z[j]] : id2[n / (w[i] / z[j])];
			g[i] -= g[k] - (j - 1);
		}

	printf("%lld\n", g[1]);
}
T2

#6053. 简单的函数

  • 注意把 f ( p ) = p − 1 f(p)=p-1 f(p)=p1拆成两个函数 g ( p ) = p , h ( p ) = 1 g(p)=p,h(p)=1 g(p)=p,h(p)=1,然后相减,这样可以保证积性函数的性质。然后就是板子了。
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = a; i <= b; i ++)

typedef long long LL;

const int N = 3e5;
const LL ny = 5e8 + 4, Mo = 1e9 + 7;

using namespace std;

bool bz[N]; int id1[N], id2[N], sqr;
LL g[N], h[N], z[N], w[N], s[N], n, m;

LL S(LL x, int y) {
	LL k = x <= sqr ? id1[x] : id2[n / x];
	LL sum = (g[k] - h[k] - (s[y - 1] - (y - 1)) + (y == 1) * 2) % Mo;
	for (LL i = y; i <= z[0] && z[i] * z[i] <= x; i ++)
		for (LL e = 1, p = z[i]; p * z[i] <= x; e ++, p *= z[i])
			sum = (sum + (z[i] ^ e) % Mo * S(x / p, i + 1) + (z[i] ^ (e + 1))) % Mo;
	return sum;
}

int main() {
	scanf("%lld", &n), sqr = int(sqrt(n));
	F(i, 2, sqr) {
		if (!bz[i])
			z[++ z[0]] = i, s[z[0]] = (s[z[0] - 1] + i) % Mo;
		F(j, 1, z[0]) {
			if (z[j] * i > sqr) break;
			bz[z[j] * i] = 1;
			if (i % z[j] == 0) break;
		}
	}
	for (LL i = 1, j; i <= n; i = j + 1) {
		j = n / (n / i); w[++ m] = n / i;
		if (w[m] <= sqr) id1[w[m]] = m; else id2[n / w[m]] = m;
		g[m] = (((w[m] + 2) % Mo * ((w[m] - 1) % Mo)) % Mo * ny) % Mo;
		h[m] = (w[m] - 1) % Mo;
	}
	F(j, 1, z[0])
		for (LL i = 1; i <= m && z[j] * z[j] <= w[i]; i ++) {
			LL k = (w[i] / z[j] <= sqr) ? id1[w[i] / z[j]] : id2[n / (w[i] / z[j])];
			h[i] = (h[i] - (h[k] - (j - 1))) % Mo;
			g[i] = (g[i] - z[j] * (g[k] - s[j - 1])) % Mo;
		}

	printf("%lld\n", n == 1 ? 1 : ((S(n, 1) + 1) % Mo + Mo) % Mo);
}
  • 两种版本的时间复杂度都是 O ( n 3 4 l o g ( n ) ) O(\frac{n^{\frac{3}{4}}}{log(\sqrt n)}) O(log(n )n43)。经实测,递归版会稍微快一些。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值