「学习笔记」Min25筛

前言

M i n 25 Min25 筛是一种能够用于求解积性函数 f ( x ) f(x) 的前缀和的方法,其前提条件是 x , n x f ( x ) \sum_{x\leq,nx是质数} f(x) 可以用 x , n x h ( x ) \sum_{x\leq,nx是质数} h(x) 简单表示。其中 h ( x ) h(x) 为完全积性函数。

过程

首先,对于每一个 n x \lfloor \frac{n}{x} \rfloor ,我们求出 x , n x h ( x ) \sum_{x\leq,nx是质数} h(x)

p i p_i 表示第 i i 小的质数, g ( N , i ) g(N,i) 表示 x n , x p i h ( x ) \sum_{x\leq n,x的最小质因子\geq p_i}h(x) 。即 N N 以内的数进行 i i 轮埃氏筛后未被筛掉的数的 h ( x ) h(x) 之和(质数未被删去),则所求即为 g ( N , P ) g(N,|P|) P |P| n \leq n 的质数个数)。

考虑如何由 g ( N , i 1 ) g(N,i-1) 得到 g ( N , i ) g(N,i) 。考虑第 i i 轮埃氏筛所筛去的数。当 p i 2 > n p_i^2 > n 时,第 i i 轮不会筛去任何数。否则,将筛去最小质因子 p i \geq p_i 的合数,所以

g ( N , i ) = g ( N , i 1 ) h ( p i ) × ( g ( N p i , i 1 ) j = 1 i 1 h ( p i ) ) g(N,i)=g(N,i-1)-h(p_i)\times(g(\lfloor \frac {N} {p_i}\rfloor,i-1)-\sum_{j=1}^{i-1}h(p_i))

因为 p i 2 N p_i^2\leq N ,所以 p i N p i p_i \leq \lfloor \frac {N} {p_i}\rfloor ,所以 p i \leq p_i 的质数也会被包含在 g ( N p i , i 1 ) g(\lfloor \frac {N} {p_i}\rfloor,i-1) 里,要加回来。

综上,

g ( N , i ) = { g ( N , i 1 ) p i 2 > n g ( N , i 1 ) h ( p i ) × ( g ( N p i , i 1 ) j = 1 i 1 h ( p i ) ) p i 2 n g(N,i)=\begin{cases} g(N,i-1)&p_i^2\gt n\\ g(N,i-1)-h(p_i)\times(g(\lfloor \frac {N} {p_i}\rfloor,i-1)-\sum_{j=1}^{i-1}h(p_i))&p_i^2\le n\end{cases}

这部分的时间复杂度是 O ( N 3 4 l o g    N ) O(\frac {N^{\frac {3} {4}}} {log \ \ N})

接下来考虑如何用上述信息求 i = 1 n f ( x ) \sum_{i=1}^n f(x)

s ( N , i ) s(N,i) 表示 j = 1 , j p i n f ( j ) \sum_{j=1,j的最小质因子\geq p_i}^n f(j)

由定义最终答案为 s ( N , 1 ) + f ( 1 ) s(N,1)+f(1)

经过上面的计算,我们已经可以快速计算 x n , x f ( x ) \sum_{x\leq n,x是质数} f(x)

考虑合数的贡献,枚举质因子个数及其出现次数,因为 f ( x ) f(x) 为积性函数,所以贡献为

j i p j 2 N e = 1 p j e + 1 N s ( N p i , j + 1 ) × f ( p i e ) + f ( p i e + 1 ) \sum_{j\geq i}^{p_j^2 \leq N}\sum_{e=1}^{p_j^{e+1}\leq N} s(\lfloor \frac {N} {p_i}\rfloor,j+1)\times f(p_i^e)+f(p_i^{e+1})

综上,

s ( N , i ) = { 0 p i > n j i p j 2 N e = 1 p j e + 1 N s ( N p i , j + 1 ) × f ( p i e ) + f ( p i e + 1 ) + x n , x f ( x ) j = 1 i 1 f ( p i ) p i n s(N,i)=\begin{cases} 0&p_i\gt n\\ \sum_{j\geq i}^{p_j^2 \leq N}\sum_{e=1}^{p_j^{e+1}\leq N} s(\lfloor \frac {N} {p_i}\rfloor,j+1)\times f(p_i^e)+f(p_i^{e+1})+\sum_{x\leq n,x是质数} f(x)-\sum_{j=1}^{i-1}f(p_i)&p_i \le n\end{cases}

这部分的时间复杂度不用记忆化也很快,是 O ( N 3 4 l o g    N ) O(\frac {N^{\frac {3} {4}}} {log \ \ N})

模板

LOJ6053

考虑如何用 x , n x h ( x ) \sum_{x\leq,nx是质数} h(x) 表示 x , n x f ( x ) \sum_{x\leq,nx是质数} f(x)

可以发现,对于质数 p p

f ( p ) = { p + 1 p = 2 p 1 p 2 f(p)=\begin{cases} p+1 & p=2 \\ p-1 & p \neq 2\end{cases}

所以,当 i > 1 i>1 时, x n , x f ( x ) j = 1 i f ( p i ) \sum_{x\leq n,x是质数} f(x)-\sum_{j=1}^if(p_i) 实际计算了 x x 以内质数的和减去减去质数的个数,筛单位函数与恒等函数即可。当 i = 1 i=1 时,给答案加上 2 2 即可。

#include <bits/stdc++.h>
using namespace std;

#define int long long 

typedef long long lint;
const int maxn = 200005, mod = 1e9 + 7, inv2 = (mod + 1) / 2;

lint n, w[maxn];
int m, S;

int id1[maxn], id2[maxn];
int vis[maxn], p[maxn], sum[maxn], tot;
int h[maxn], g[maxn];

inline lint gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	lint sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

void pre(int n)
{
	for (int i = 2; i <= n; ++i) {
		if (!vis[i]) p[++tot] = i, sum[tot] = sum[tot - 1] + i;
		for (int j = 1; i * p[j] <= n; ++j) {
			vis[i * p[j]] = 1;
			if (i % p[j] == 0) break;
		}
	}
}

int s(int x, int i)
{
	if (x <= 1 || p[i] > x) return 0;
	int k = x <= S ? id1[x] : id2[n / x];
	int res = ((g[k] - h[k] - (sum[i - 1] - (i - 1))) % mod + mod) % mod;

	if (i == 1) res += 2;
	lint p1, p2;
	for (int j = i; j <= tot && (lint)p[j] * p[j] <= x; ++j){
		p1 = p[j]; p2 = p1 * p[j];
		for (lint e = 1; p2 <= x; p1 = p2, p2 *= p[j], ++e) {
			res += ((lint)s(x / p1, j + 1) * (p[j] ^ e) + (p[j] ^ (e + 1))) % mod;
			if (res >= mod) res -= mod;
		}
	}

	return res;
}

signed main()
{
	n = gi(); S = sqrt(n);

	pre(S);

	for (lint i = 1, j; i <= n; i = j + 1) {
		j = n / (n / i);
		w[++m] = n / i;
		if (w[m] <= S) id1[w[m]] = m;
		else id2[n / w[m]] = m;
		h[m] = (w[m] - 1) % mod;
		g[m] = (lint)(w[m] + 2) % mod * ((w[m] - 1) % mod) % mod * inv2 % mod;
	}

	for (int j = 1; j <= tot; ++j)
		for (int i = 1; i <= m && (lint)p[j] * p[j] <= w[i]; ++i) {
			int k = (w[i] / p[j] <= S) ? id1[w[i] / p[j]] : id2[n / (w[i] / p[j])];
			h[i] = ((lint)h[i] - h[k] + j - 1 + mod) % mod;
			g[i] = (g[i] - (lint)p[j] * (g[k] - sum[j - 1]) % mod + mod) % mod;
		}

	printf("%lld\n", s(n, 1) + 1);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/84935201
今日推荐