Epiphyllum

Min_25筛

2024-10-01

Min_25筛#

Min_25筛是用来解决积性函数前缀和的问题,可以做到 n34logn\frac{n^\frac{3}{4}}{logn} 的复杂度(1e11)

积性函数 当 gcd(a,b)=1gcd(a,b) = 1f(x)=f(a)f(b)f(x) = f(a) f(b)

要求f(x),xPrimf(x), x \in Prim 可以快速计算

要做两步事情

1、计算 2n[xPrim]F(x)\sum_{2}^{n}[ x\in Prim]{F(x)}

2、计算2nF(x)\sum_{2}^{n}{F(x)}

首先考虑怎么计算第一步的答案

sieve dp#

我们定义 g(n,j)g(n, j)i=2n[iPrimorminprims(i)>Pj]F(i)\sum_{i=2}^{n} [i \in Prim \, or \, minprims(i) > P_j] F(i)

人话就是 ii 要么是质数, 要么最小质因子大于第jj个质数

意思就是 质数和还没被前jj个数筛掉的 F(x)F(x) 的和

当前状态是g(n,j)g(n, j)

考虑转移 就像是模拟埃氏筛

  1. 如果jj 不是质数 g(n,j)=g(n,j1)g(n, j) = g(n, j - 1)

  2. 如果jj 是质数

    • ​ 如果j2>nj^2 > ng(n,j)=g(n,j1)g(n, j) = g(n, j - 1), 因为它能筛掉的数至少是 j2j ^ 2

    • 否则 它可以筛掉的数可以写成 j×mj \times m 其中m的最小质因子要大于等于jj

      ​ 即minprims(m)jminprims(m) \ge j

      ​ 所以

      g(n,j)=j×(g(nj,j1)g(j1,j1))g(n, j) = j \times (g(\lfloor \frac{n}{j}\rfloor, j - 1) - g(j -1, j -1))

      g(nj,j1)g(\lfloor \frac{n}{j}\rfloor, j - 1)表示 mm 中没被筛掉的数的和 就是j1j -1 中的质数和最小质因子要大于等于jj

      ​ 所以减掉g(j1,j1)g(j -1, j -1) 然后乘上jj 就是答案

在第二个转移转移过程中 有个 乘 jj 这是因为我们本来计算的就是质数的和, 非质数不在我们的计算范围之内我们应该筛掉它,筛掉它,那么它的值应该质因子值的乘积才对

常见 就是 f(i) = i, f(i) = 1等

来分析下复杂度 首先只要n\sqrt n 以内的质数就可以把 它全部筛完, 因为大于n\sqrt n的质数最多只有一个,而它不会筛掉任何数 所以1jn1 \le j \le \sqrt n 又因为nj\lfloor \frac{n}{j}\rfloornab=nab \lfloor \frac{\lfloor \frac{n}{a}\rfloor}{b} \rfloor = \lfloor \frac{n}{ab} \rfloor 所以至多只有n\sqrt n 个数, 只有 j2nj ^2 \le n 才会发生转移

所以n34logn\frac{n^\frac{3}{4}}{logn} 的复杂度

样例代码,其中hh 函数是 f(x)=1f(x) = 1 , gg 函数是f(x)=if(x) = i

constexpr int mod = 1e9 + 7, maxn = 1e5 + 5;
using mint = ModInt<mod>;
vector<int>prim;
vector<mint>sump(1);
vector<int>vis(maxn);
void init(int n){
	vis[1] = 1;
	for(int i = 2; i <= n; i++){
		if(!vis[i]){
			prim.push_back(i);
			sump.push_back(sump.back() + i);
		}
		for(int j = 0; prim[j] * i <= n; j++){
			vis[i * prim[j]] = 1;
			if(i % prim[j] == 0)break;
		}
	}
}
void solve(){
    int n; cin >> n;
    int B = sqrt(n);
    init(B);
    vector<int>id1(B + 1), id2(B + 1);
    vector<int>w(2 * B + 1);
    vector<mint>g(2 * B + 1), h(2 * B + 1);
    int tot = 0;
    for(int i = 1, j; i <= n; i = j + 1){
    	j = n / (n / i);
    	w[++tot] = n / i;
    	h[tot] = mint(w[tot] - 1);
    	g[tot] = mint(w[tot]) * (w[tot] + 1) * mint(2).inv() - 1;
    	if(w[tot] <= B)id1[w[tot]] = tot;
    	else id2[j] = tot;
    }
    int sz = prim.size();
    for(int i = 0; i < sz; i++){
    	for(int j = 1; j <= tot; j++){
    		if(prim[i] * prim[i] > w[j])break;
    		int k = w[j] / prim[i];
    		if(k <= B) k = id1[k];
    		else k = id2[n / k];
    		h[j] -= h[k] - i;
    		g[j] -= prim[i] * (g[k] - sump[i]);
    	}
    }

第二步 Min25筛#

考虑把上面的dp倒过来加入被筛掉的数

我们定义s(n,j)s(n, j) 表示22 ~ nn 中 最小质因子大于等于 第jj 个质数 的数 和质数的 F(x)F(x) 的和

那么最终答案就是 s(n,0)s(n, 0)

同样的考虑只有 j2nj^2 \le n 时才发生转移

​ 被jj 筛掉的数的形式 长 jk×mj^k \times m 其中mm 的最小质因子大于 jj 注意这里是大于(保证互质), 上面的是大于等于

​ 那么有

s(n,j1)=s(n,j)+1c,jc+1nf(jc)×(s(njc,j)Fprim(j))+f(jc+1)s(n, j - 1) = s(n, j) + \sum_{1\le c,\, j^{c+1} \le n} f(j^c) \times (s(\lfloor \frac{n}{j^c} \rfloor, j) - F_{prim}(j)) \, +\, f(j^{c + 1})

下面介绍一种递归的实现方式

s(n,j)s(n,j)分为两个部分计算,一部分是所有质数的和,一部分是所有合数的和

我们定义s(n,j)s(n, j) 表示22 ~ nn 中 最小质因子大于等于 第jj 个质数 的数 的 F(x)F(x) 的和

那么转移里面的(s(njc,j)Fprim(j))(s(\lfloor \frac{n}{j^c} \rfloor, j) - F_{prim}(j)) 可以直接用 s(njc,j)s(\lfloor \frac{n}{j^c} \rfloor, j) 替代

auto Min_25 = [&](auto self, int x, int y) -> mint{
    	if(x <= 1)return 0;
    	int k = x;
    	if(k <= B) k = id1[k];
    	else k = id2[n / k];
    	mint ret = g[k] - sump[y] - h[k] + y;//计算质数的贡献
    	if(y == 0)ret += 2;
    	for(int i = y; i < sz; i++){
    		if(prim[i] * prim[i] > x)break;
    		int t1 = prim[i];
    		int t2 = prim[i] * prim[i];
    		for(int e = 1; t2 <= x; e++){
                //计算合数的贡献
    			ret += self(self, x / t1, i + 1) * (prim[i] ^ e) + (prim[i] ^ (e + 1));
    			t1 = t2;
    			t2 *= prim[i];
    		}
    	}
    	return ret;
    };
Min_25筛
https://epiphyllum.masttf.fun/post/Min_25筛
作者
Masttf
发布于
10/1/2024
许可协议
CC BY-NC-SA 4.0