Preface
-
模拟赛遇到了,于是学习了一下。
-
问题一般是求积性函数 f ( x ) f(x) f(x)的前缀和,如 ∑ i = 1 n F ( i ) \sum_{i=1}^nF(i) i=1∑nF(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的简单多项式 p∈Pf(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=2∑nF(i)[i的最小质因子大于等于Pj]
-
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=1∑n[i∈P,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,j−1)g(n,j−1)−F(Pj)[g(Pjn,j−1)−∑i=1j−1F(Pi)]Pj2>nPj2≤n
-
现在还有一个问题是 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=1∑nF(i)
-
我们要求质数的贡献,实质上就是 g ( n , ∣ P ∣ ) − g ( P j − 1 , j − 1 ) g(n,|P|)-g(P_{j-1},j-1) g(n,∣P∣)−g(Pj−1,j−1)
-
那么可以推出 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=1∑j−1f(Pi)+k≥j∑∣P∣e∑(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=2∑nF(i)[i的最小质因子不小于pi或i是质数].
-
可以得到: 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)+∑e≥1且 pie+1 ≤n F(pie)[S(⌊pien⌋,i+1)−g(pi,i)]+F(pie+1)S′(n,i+1)pi2≤npi2≥n
-
初值 S ′ ( n , ∣ P ∣ + 1 ) = g ( n , ∣ P ∣ ) S'(n,|P|+1)=g(n,|P |) S′(n,∣P∣+1)=g(n,∣P∣)
Some Problems
T1
事实上就是求 1 ∼ n 1\sim n 1∼n素数,这个可以直接通过求的 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
- 注意把 f ( p ) = p − 1 f(p)=p-1 f(p)=p−1拆成两个函数 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)。经实测,递归版会稍微快一些。