一点数论喵

news/2025/2/10 9:10:27/文章来源:https://www.cnblogs.com/Pentimentqwq/p/18707100

数论函数

数论函数就是定义域为 \(\mathbb Z_+\) 的函数,通常值域为 \(\mathbb N\)

常见的数论函数有:

  • \(\varepsilon(x)=[x=1]\)
  • \(1(x)=1\)
  • \(\text{id}_k(x)=x^k\)\(\text{id}_1\) 通常简记为 \(\text{id}\)\(\text{id}_0\)\(1\)
  • \(\Omega(x)\) 表示 \(x\) 的素因子个数;
  • \(\omega(x)\) 表示 \(x\) 的不同素因子个数;
  • \(\tau(x)\) 表示 \(x\) 的因数个数;
  • \(\sigma_k(x)\) 表示 \(x\) 的所有因数的 \(k\) 次方和,\(\sigma_1\) 通常简记为 \(\sigma\)\(\sigma_0\)\(\tau\)
  • \(\varphi(x)=\sum\limits_{i=1}^x[i\perp x]\)
  • \(\mu(x)=[\forall p\in\mathbb P,p^2\nmid x](-1)^{\omega(x)}\)

若数论函数 \(f\) 满足,对于 \(x\perp y\),有 \(f(xy)=f(x)f(y)\),则称 \(f\) 是积性的。

\(\varphi\) 的积性通过其计算式 \(\varphi\left(\prod p^k\right)=\prod p^k\left(1-\dfrac1p\right)\) 即可得到。\(\mu\) 的积性分讨几种情况即可。

一些常见的等式:

  • \(\sum\limits_{d|x}\varphi(x)=x\)。对 \(x\) 素因子分解,由 \(\varphi\) 的积性即得。
  • \(\sum\limits_{d|x}\mu(x)=\varepsilon(x)\),注意到有重复素因子的数不会产生贡献,故转化成对集合的枚举。设 \(k=\omega(x)\),答案为 \(\sum\limits_{i=0}^{k}\dbinom ki(-1)^i=(1+(-1))^k=[k=0]\),则当且仅当 \(n=1\) 时原式为 \(1\),否则为 \(0\)

整除分块

欲求 \(\sum\limits_{i=1}^nf\left(\left\lfloor\dfrac{n}{i}\right\rfloor\right)\),需要 \(\mathcal O(T(n)\sqrt n)\) 复杂度,其中 \(T(n)\) 为计算 \(f\) 的前缀和的复杂度。

\(\left|\left\{\left.\left\lfloor\dfrac{n}{i}\right\rfloor\right|1\le i\le n\right\}\right|=\mathcal O(\sqrt n)\)。分讨 \(i\le\sqrt n\)\(i>\sqrt n\) 的情况立即得到。

\(k=\left\lfloor\dfrac{n}{i}\right\rfloor\),则使得 \(\left\lfloor\dfrac{n}{j}\right\rfloor=k\) 的最大 \(j\)\(\left\lfloor\dfrac{n}{k}\right\rfloor\)。证明的话,由 \(\left\lfloor\dfrac{n}{k}\right\rfloor\ge\left\lfloor\dfrac{n}{\frac ni}\right\rfloor=\lfloor i\rfloor=i\) 即得。

故可以以 \(\mathcal O(\sqrt n)\) 的复杂度枚举每一段,对每一段统计 \(f\) 的前缀和即可。

欧拉反演

即上文中的 \(\sum\limits_{d|x}\varphi(x)=x\)

例如要求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m\gcd(i,j)\),有:

\[\begin{aligned} \sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)&=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|\gcd(i,j)}\varphi(d)\\ &=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i,d|j}\varphi(d)\\ &=\sum_{d=1}^{\min(n,m)}\varphi(d)\left\lfloor\dfrac nd\right\rfloor\left\lfloor\dfrac md\right\rfloor \end{aligned} \]

直接整除分块即可。

莫比乌斯反演

\(g(n)=\sum\limits_{d|n}f(d)\),则有 \(f(n)=\sum\limits_{d|n}\mu(d)g\left(\dfrac nd\right)\)

证明:

\[\sum_{d|n}\mu(d)g\left(\dfrac nd\right)=\sum_{d|n}\mu(d)\sum_{d'|\frac nd}f(d')=\sum_{d'|n}f(d')\sum_{d|\frac{n}{d'}}\mu(d) \]

由于 \(\sum\limits_{d|x}\mu(x)=\varepsilon(x)\),故原式当且仅当 \(d'=n\) 时第二个和式不为 \(0\),故结果为 \(f(n)\)

\(\gcd\) 的二重求和为例:

\[\sum_{i=1}^n\sum_{j=1}^mf(\gcd(i,j)) \]

\(f(n)=\sum\limits_{d|n}g(d)\),按照上面的套路,答案即为:

\[\sum_{d=1}^{\min(n,m)}g(d)\left\lfloor\dfrac nd\right\rfloor\left\lfloor\dfrac md\right\rfloor \]

例如欧拉反演里举的例子就是 \(f=\text{id}\) 的情况,这时 \(g=\varphi\)

P4449 于神之怒加强版

题意:求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m\gcd(i,j)^k\)

答案为:

\[\sum_{i=1}^{\min(n,m)}\left\lfloor\dfrac ni\right\rfloor\left\lfloor\dfrac mi\right\rfloor\sum_{d|i}d^k\mu\left(\dfrac{i}{d}\right) \]

考虑线性筛出后面那一坨东西。对于素数的幂,有 \(f(p^x)=p^{kx}-p^{k(x-1)}=(p^k-1)p^{k(x-1)}\),那么当 \(x>1\) 时有 \(f(p^x)=p^xf(p^{x-1})\),当 \(x=1\) 时有 \(f(p)=p^k-1\)

故有:

\[f(np)=\left\{ \begin{aligned} &p^kf(n)&p\mid n\\ &(p^k-1)f(n)&p\nmid n \end{aligned} \right. \]

线性筛后整除分块即可,时间复杂度为 \(\mathcal O(n+T\sqrt n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e6 + 5, p = 1e9 + 7;
int T, n, m, k, tot, pri[N], f[N];
bool vis[N];
int qpow(int a, int b) {int c = 1;while (b) {if (b & 1) c = (ll)c * a % p;a = (ll)a * a % p, b >>= 1;}return c;
}
void sieve(int n) {f[1] = 1;for (int i = 2, x; i <= n; i++) {if (!vis[i]) pri[++tot] = i, f[i] = qpow(i, k) - 1;for (int j = 1; j <= tot && i * pri[j] <= n; j++) {vis[x = i * pri[j]] = 1;if (!(i % pri[j])) { f[x] = (ll)f[i] * (f[pri[j]] + 1) % p; break; }f[x] = (ll)f[i] * f[pri[j]] % p;}}for (int i = 1; i <= n; i++) f[i] = (f[i - 1] + f[i]) % p;
}
int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T >> k, sieve(N - 1);while (T--) {cin >> n >> m;int u = min(n, m), sum = 0;for (int l = 1, r; l <= u; l = r + 1) r = min(n / (n / l), m / (m / l)), sum = (sum + (ll)(n / l) * (m / l) % p * (f[r] - f[l - 1]) % p + p) % p;cout << sum << '\n';}
}

这里没有 Tricks,但是有一个警示后人:后面要用到前面的 \(f\) 值时就不要边筛边做前缀和。

P3312 [SDOI2014] 数表

题意:定义 \(f(i,j)=\sigma(\gcd(i,j))\),求:

\[\sum_{i=1}^n\sum_{j=1}^m[f(i,j)\le a]f(i,j) \]

原式即为:

\[\sum_{i=1}^{\min(n,m)}\left\lfloor\dfrac ni\right\rfloor\left\lfloor\dfrac mi\right\rfloor\sum_{d|i}[\sigma(d)\le a]\sigma(d)\mu\left(\dfrac id\right) \]

设后面那一坨是 \(g(i)\),将询问按 \(a\) 扫描线,每一次加入新的 \(\sigma(d)\),会对其所有倍数处的 \(g\) 产生贡献,查询前缀和,使用树状数组。

时间复杂度为 \(\mathcal O(q\sqrt n\log n+n\log^2 n)\)

#include <bits/stdc++.h>
using namespace std;
#define int unsigned
typedef long long ll;
const int N = 100005, M = 20005, qwq = 1e5;
int Q, tot, pri[N], mu[N], t[N], ans[N];
bool vis[N];
struct query { int id, n, m, a; } q[M];
bool cmp(query a, query b) { return a.a < b.a; }
pair<ll, int> p[N];
void sieve(int n) {mu[1] = 1;for (int i = 2, k; i <= n; i++) {if (!vis[i]) pri[++tot] = i, mu[i] = -1;for (int j = 1; j <= tot && i * pri[j] <= n; j++) {vis[k = i * pri[j]] = 1;if (!(i % pri[j])) { mu[k] = 0; break; }mu[k] = -mu[i];}}for (int i = 1; i <= n; i++) {p[i].second = i;for (int j = i; j <= n; j += i) p[j].first += i;}sort(p + 1, p + 1 + n);
}
void upd(int x, int d) { while (x <= qwq) t[x] += d, x += x & -x; }
int qry(int x) { int y = 0; while (x) y += t[x], x -= x & -x; return y; }
signed main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> Q;for (int i = 1; i <= Q; i++) cin >> q[i].n >> q[i].m >> q[i].a, q[i].id = i;sort(q + 1, q + 1 + Q, cmp);sieve(qwq);for (int i = 1, j = 1; i <= Q; i++) {while (p[j].second && p[j].first <= q[i].a) {int x = p[j].second;for (int k = 1; x * k <= qwq; k++) upd(x * k, (int)p[j].first * mu[k]);j++;}int n = q[i].n, m = q[i].m, u = min(n, m);for (int l = 1, r; l <= u; l = r + 1) {r = min(n / (n / l), m / (m / l));ans[q[i].id] += (n / l) * (m / l) * (qry(r) - qry(l - 1));}}for (int i = 1; i <= Q; i++) cout << (ans[i] & INT_MAX) << '\n';
}

VLATTICE - Visible Lattice Points

题意:三维空间中有 \((n+1)^3\) 个点,坐标从 \((0,0,0)\)\((n,n,n)\),问有多少个点可以从 \((0,0,0)\) 看到而不受遮挡。

分点的坐标讨论,答案为:

\[3+3\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=1]+\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^n[\gcd(i,j,k)=1] \]

考虑如何求解下式:

\[\sum_{i_1=1}^n\sum_{i_2=1}^n\dots\sum_{i_k=1}^n[\gcd(i_1,i_2,\dots,i_k)=1] \]

由于 \(\sum\limits_{d|n}\mu(d)=[n=1]\),有:

\[\sum_{i_1=1}^n\sum_{i_2=1}^n\dots\sum_{i_k=1}^n\sum_{d|i_1,\dots,d|i_k}\mu(d) \]

交换求和符号:

\[\sum_{d=1}^n\mu(d)\left\lfloor\dfrac{n}{d}\right\rfloor^k \]

使用整除分块计算即可,时间复杂度为 \(\mathcal O(T\sqrt n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int T, n, tot, pri[N], mu[N];
bool vis[N];
void sieve(int n) {mu[1] = 1;for (int i = 2, k; i <= n; i++) {if (!vis[i]) pri[++tot] = i, mu[i] = -1;for (int j = 1; j <= tot && i * pri[j] <= n; j++) {vis[k = i * pri[j]] = 1;if (!(i % pri[j])) { mu[k] = 0; break; }mu[k] = -mu[i];}}for (int i = 1; i <= n; i++) mu[i] += mu[i - 1];
}
ll sm2(int n) {ll sum = 0;for (ll l = 1, r, x; l <= n; l = r + 1) {r = n / (x = n / l);sum += (mu[r] - mu[l - 1]) * x * x;}return sum;
}
ll sm3(int n) {ll sum = 0;for (ll l = 1, r, x; l <= n; l = r + 1) {r = n / (x = n / l);sum += (mu[r] - mu[l - 1]) * x * x * x;}return sum;
}
int main() {sieve(1e6);cin >> T;while (T--) {cin >> n;cout << 3 + 3 * sm2(n) + sm3(n) << '\n';}
}

狄利克雷卷积 & DGF

对于数论函数 \(f,g\),定义狄利克雷卷积 \(f*g\) 为:

\[(f*g)(x)=\sum_{d\mid x}f(x)g\left(\dfrac nx\right) \]

狄利克雷卷积显然满足交换律,结合律和对 \(+\) 的分配律的证明也是容易的。

一些例子是 \(1*1=\tau\)\(1*\text{id}_k=\sigma_k\)\(1*\varphi=\text{id}\)\(1*\mu=\varepsilon\)

\(f*g=h\),则记 \(f=h/g\),下文中 \(/\) 均指狄利克雷除法。

类比多项式乘法和 OGF,考虑对狄利克雷卷积构造一种生成函数。

由于这里是下标乘法,故生成函数的 \(x\) 须在指数上,故有 DGF 的定义:

\[\text{DGF}(f)(x)=\sum_{i=1}^{\infty}\dfrac{f_i}{i^x} \]

容易验证两个数论函数做狄利克雷卷积等价于它们的 DGF 相乘。

例如 \(\text{DGF}(1)=\zeta\),其中 \(\zeta\) 是黎曼 zeta 函数,定义为:

\[\zeta(x)=\sum_{i=1}^\infty\dfrac{1}{i^x} \]

更多的例子:

  • \(\text{DGF}(1)(x)=\zeta(x)\)
  • \(\text{DGF}(\text{id}_k)(x)=\sum\limits_{i=1}^\infty\dfrac{x^k}{i^x}=\sum\limits_{i=1}^\infty\dfrac{1}{i^{x-k}}=\zeta(x-k)\)
  • \(\text{DGF}(\tau)(x)=\text{DGF}(1*1)(x)=\zeta^2(x)\)
  • \(\text{DGF}(\sigma_k)(x)=\text{DGF}(1*\text{id}_k)(x)=\zeta(x)\zeta(x-k)\)
  • \(\text{DGF}(\varepsilon)(x)=1\)
  • \(\text{DGF}(\varphi)(x)=\text{DGF}(\text{id}/1)(x)=\dfrac{\zeta(x-1)}{\zeta(x)}\)
  • \(\text{DGF}(\mu)(x)=\text{DGF}(\varepsilon/1)(x)=\dfrac{1}{\zeta(x)}\)

用 DGF 可以推出各种奇奇怪怪的式子,譬如莫比乌斯反演的本质就是把 \(f*1=g\) 左边的 \(1\) 除了过去变成 \(f=g*\mu\)

杜教筛

现在欲快速求出 \(\sum\limits_{i=1}^nf(i)\),一种方式是杜教筛,要求存在一数论函数 \(g\),使得 \(g\)\(f*g\) 的前缀和可以 \(\mathcal O(1)\) 计算,如 \(1*\varphi=\text{id}\)

推式子的过程比较 simple,设 \(h=f*g\),先列出 \(h\) 的前缀和的式子:

\[\begin{aligned} \sum_{i=1}^nh(i)&=\sum_{i=1}^n\sum_{d|i}g(d)f\left(\dfrac id\right)\\ &=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}f(i)\\ &=g(1)\sum_{i=1}^nf(i)+\sum_{d=2}^ng(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}f(i) \end{aligned} \]

通常来说 \(g(1)=1\),移项得:

\[\sum_{i=1}^nf(i)=\sum_{i=1}^nh(i)-\sum_{d=2}^ng(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}f(i) \]

最震撼人心的一步在于,只要我们线性筛出 \(\mathcal O(n^{\frac23})\) 以内的 \(f\) 的前缀和,并且加上记忆化,那么我们就可以直接递归求解 \(\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}f(i)\),复杂度为 \(\mathcal O(n^{\frac23})\)。如何证明?

首先有引理:设 \(S(n)=\left|\left\{\left.\left\lfloor\dfrac{n}{i}\right\rfloor\right|1\le i\le n\right\}\right|\),则 \(\forall m\in S(n),S(m)\subseteq S(n)\)。这一点等价于证 \(\left\lfloor\dfrac{\lfloor\frac ab\rfloor}{c}\right\rfloor=\left\lfloor\dfrac{a}{bc}\right\rfloor\),设 \(\dfrac ab-\left\lfloor\dfrac{a}{b}\right\rfloor=r\),由于 \(\dfrac rc<1\) 即得。

故由于记忆化,每个 \(\left\lfloor\dfrac{n}{i}\right\rfloor\) 只会被求一次。设预处理出 \(m\) 以内的 \(f\) 的前缀和,其中 \(m>\sqrt n\),则有:

\[\begin{aligned} T(n)&=\mathcal O(m)+\sum_{i=1}^{\lfloor\frac nm\rfloor}\mathcal O\left(\sqrt{\dfrac ni}\right)\\ &=\mathcal O\left(m+\int_0^{\frac nm}\sqrt{\dfrac nx}\text{d}x\right)\\ &=\mathcal O\left(m+\dfrac{n}{\sqrt m}\right) \end{aligned} \]

由基本不等式,当 \(m=\mathcal O(n^\frac{2}{3})\) 时有最优复杂度 \(\mathcal O(n^\frac{2}{3})\)

例如要求 \(\varphi\)\(\mu\) 的前缀和,就将 \(1*\varphi=\text{id}\)\(1*\mu=\varepsilon\) 代进去就行了。

P4318 完全平方数

求第 \(k\) 个不是完全平方数的倍数的数。

不是完全平方数等价于 \(\mu(x)\not=0\),但这个不好求和,这里有一个 Trick,注意到 \(\mu(x)=\{-1,0,1\}\),那么有 \(\mu^2(x)=[\mu(x)\not=1]\)

故要求使得 \(\sum\limits_{i=1}^n\mu^2(i)\ge k\) 的最小的 \(n\),二分。

考虑找一个函数 \(f\)\(\mu^2\) 贴贴,我们希望 \(\mu^2*f=1\),比如平方判定函数 \(f(x)=[x=m^2,m\in\mathbb Z_+]\),当且仅当 \(d\)\(x\) 的最大平方因子时 \(f(d)\mu^2\left(\dfrac nd\right)=1\),否则为 \(0\),证明比较容易,分讨 \(f,\mu^2\) 的取值情况即可。

根据杜教筛有:

\[\sum_{i=1}^n\mu^2(i)=n-\sum_{d=2}^nf(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\mu^2(i) \]

由于 \(f\) 是判定函数,仅枚举平方数

\[\sum_{i=1}^n\mu^2(i)=n-\sum_{d=2}^{\lfloor\sqrt n\rfloor}\sum_{i=1}^{\lfloor\frac{n}{d^2}\rfloor}\mu^2(i) \]

直接做即可,时间复杂度为 \(\mathcal O(Tn^{\frac23}\log n)\),可以过。

#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
const int N = 1e6 + 5;
int T, k, tot, mu2[N], pri[N];
bool vis[N];
gp_hash_table<int, int> mp;
void sieve(int n) {mu2[1] = 1;for (int i = 2, k; i <= n; i++) {if (!vis[i]) pri[++tot] = i, mu2[i] = 1;for (int j = 1; j <= tot && i * pri[j] <= n; j++) {vis[k = i * pri[j]] = 1;if (!(i % pri[j])) { mu2[k] = 0; break; }mu2[k] = mu2[i];}}for (int i = 1; i <= n; i++) mu2[i] += mu2[i - 1];
}
int djs(int n) {if (n < N) return mu2[n];if (mp[n]) return mp[n];int sum = n;for (int d = 2; d * d <= n; d++) sum -= djs(n / (d * d));return mp[n] = sum;
}
void solve() {cin >> k;ll L = 1, R = 1.7e9, mid, ans;while (L <= R) {mid = (L + R) >> 1;if (djs(mid) >= k) ans = mid, R = mid - 1;else L = mid + 1;}cout << ans << '\n';
}
int main() {sieve(N - 1);ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> T;while (T--) solve();
}

Trick:值域为 \(\{-1,0,1\}\) 的函数 \(f\)\(f^2(x)=[f(x)\not=0]\);对于判定函数 \(f\),可以仅枚举满足 \(f(x)=1\) 的数以优化复杂度。

AT_xmascon19_d Sum of (-1)^f(n)

题意:定义 \(\lambda(n)=(-1)^{\Omega(n)}\),求 \(\sum\limits_{i=1}^n\lambda(i)\)

刘维尔函数 \(\lambda\) 是完全积性的,考虑构造 \(\lambda*g=h\)。尝试令 \(g=1\),则:

\[h(p^k)=\sum_{i=0}^k(-1)^i=[2|k] \]

由于 \(\lambda,1\) 都是积性的,则 \(h\) 也是积性的,实际上有 \(h(n)=[n=m^2,m\in\mathbb Z_+]\),即平方和判定函数。这个函数在 P4318 中也有出现。

得到结论 \(\lambda*1=h\)。事实上有 \(h\) 的 DGF 为 \(\zeta(2x)\),那么 \(\lambda\) 的 DGF 就是 \(\dfrac{\zeta(2x)}{\zeta(x)}\),结合 P4318 的结论可以得出 \(\mu^2\) 的 DGF 是 \(\dfrac{\zeta(x)}{\zeta(2x)}\),故有 \(\lambda*\mu^2=\varepsilon\)

\(1\)\(h\) 都方便求前缀和,直接使用杜教筛即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e7 + 5;
int tot, pri[N], lv[N];
bool vis[N];
ll n;
unordered_map<ll, ll> mp;
ll djs(ll n) {if (n < N) return lv[n];if (mp[n]) return mp[n];ll sum = sqrtl(n);for (ll l = 2, r, k; l <= n; l = r + 1) r = n / (k = n / l), sum -= djs(k) * (r - l + 1);return mp[n] = sum;
}
void sieve(int n) {lv[1] = 1;for (int i = 2; i <= n; i++) {if (!vis[i]) pri[++tot] = i, lv[i] = -1;for (int j = 1; j <= tot && i * pri[j] <= n; j++) {vis[i * pri[j]] = 1, lv[i * pri[j]] = -lv[i];if (!(i % pri[j])) break;}lv[i] += lv[i - 1];}
}
int main() {sieve(N - 1);cin >> n, cout << djs(n);
}

Trick:将刘维尔函数和平方和判定函数、\(\mu^2\) 等一起考虑。


附:这里提到的性质,杜教筛求的是 \(f\) 的块筛(一次杜教筛筛出了所有 \(x=\left\lfloor\dfrac{n}{i}\right\rfloor\) 处的点值),故若后面只用到了块筛值,则只有一开始预处理时有 \(\mathcal O(n^\frac{2}{3})\) 的复杂度。

贝尔级数

对于积性函数 \(f\),我们一般只需要关心 \(f\)\(p^k\) 处的取值,考虑构造关于 \(p^k\) 的级数。

定义积性函数 \(f\),其在模 \(p\) 意义下的贝尔级数为:

\[F_p(x)=\sum_{i=0}^\infty x^if(p^i) \]

容易验证,在模 \(p\) 意义下,两个数论函数做狄利克雷卷积等价于它们的贝尔级数相乘。

贝尔级数相对于 DGF 的优点在于,前者是多项式的形式,故可以用研究普通生成函数的方式研究贝尔级数。

一些常见数论函数的贝尔级数:

  • \(\varepsilon\Rightarrow \text{E}_p(x)=1\)
  • \(1\Rightarrow \text{I}_p(x)=\dfrac{1}{1-x}\)
  • \(\text{id}_k\Rightarrow (\text{Id}_k)_p(x)=\dfrac{1}{1-p^kx}\)
  • \(\tau\Rightarrow\text{T}_p(x)=\dfrac{1}{(1-x)^2}\)
  • \(\sigma_k\Rightarrow\Sigma_k(x)=\dfrac{1}{(1-x)(1-p^kx)}\)
  • \(\varphi\Rightarrow (\Phi_k)_p(x)=\dfrac{1-x}{1-px}\)
  • \(\mu\Rightarrow \text{M}_p(x)=1-x\)
  • \(\mu^2\Rightarrow(\text{M}^2)_p(x)=1+x\)
  • \(\lambda\Rightarrow\Lambda_p(x)=\dfrac{1}{1+x}\)
  • \(k\) 次方判定函数 \(\text{chk}_k\Rightarrow(\text{Chk}_k)_p(x)=\dfrac{1}{1-x^k}\)

可以类比 DGF,使用贝尔级数研究数论函数之间做卷积的结果,或者当 \(k\) 上界不大时可以直接使用多项式乘法快速得到两个数论函数卷积后在素数幂处的取值。

定义数论函数点积 \(f\cdot g\) 为:

\[(f\cdot g)(x)=f(x)g(x) \]

对于数论函数 \(f\),其贝尔级数为 \(\text{F}_p(x)\),则 \(f\cdot\varepsilon\) 的贝尔级数为 \(\text{E}_p(x)\)\(f\cdot\text{id}_k\) 的贝尔级数为 \(\text{F}_p(p^kx)\)

Powerful Number 筛

如果正整数 \(n\) 的素因子分解式中每个素因子的次数都大于 \(1\),则称 \(n\) 是 Powerful Number。

每个 Powerful Number 都可以被写成 \(a^2b^3\) 的形式。证明:对于 \(p^k\),若 \(2|k\) 则将 \(a\) 乘上 \(p^{\frac k2}\),否则将 \(a\) 乘上 \(p^{\frac{k-3}{2}}\),将 \(b\) 乘上 \(p\)

定理:\(n\) 以内的 Powerful Number 有 \(\mathcal O(\sqrt n)\) 个。

证明:枚举 \(a\)

\[\mathcal O\left(\sum_{i=1}^{\sqrt n}\sqrt[3]{\dfrac{n}{x^2}}\right)=\mathcal O\left(\int_1^{\sqrt n}\sqrt[3]{\dfrac{n}{x^2}}\text{d}x\right)=\mathcal O(\sqrt n) \]

找 Powerful Number 的过程直接暴力搜索出各素数的指数即可,时间复杂度为 \(\mathcal O(\sqrt n)\)

顺带一提,Powerful Number 的补集叫 Square Free Number,就是 P4318 中计数的那个东西。

Powerful Number 筛用于求出一些积性函数的前缀和,如果要求 \(f\) 的前缀和,需要找到一个积性函数 \(g\),使得其在素数处的取值和 \(f\) 相等,并且能快速求出 \(g\) 的块筛。

\(h=f/g\),由于 \(f,g\) 都是积性函数,则 \(h\) 也是积性函数,则 \(h(1)=1\)

对于素数 \(p\)\(f(p)=g(1)h(p)+g(p)h(1)=g(p)+h(p)\),由 \(f(p)=g(p)\) 推出 \(h(p)=0\)。由 \(h\) 是积性函数推出 \(h(x)\) 仅当 \(x\) 为 Powerful Number 时不为 \(0\),这样的 \(x\) 仅有 \(\mathcal O(\sqrt n)\) 个。

由于 \(\sum\limits_{i=1}^nf(i)=\sum\limits_{d=1}^nh(d)\sum\limits_{i=1}^{\lfloor\frac nd\rfloor}g(i)\),有值的 \(h(d)\) 仅有 \(\mathcal O(\sqrt n)\) 个直接枚举,如果 \(g\) 的前缀和可以直接 \(\mathcal O(1)\) 求那复杂度就是 \(\mathcal O(\sqrt n)\) 的,否则需要求出 \(g\) 的块筛,比如用杜教筛就是 \(\mathcal O(n^{\frac23})\) 的。

P5325 【模板】Min_25 筛

【模板】Powerful Number 筛

其实 PN 筛和 Min_25 筛对 \(f\) 的要求差不多是一样的,所以很多时候都可以在 Min_25 的题里看到 PN 筛的身影。但是跑不过 Min25 啊 qwq

这个题基本上直接把 \(g\) 给我们了,不难看出有 \(g(n)=n\varphi(n)\)

为了求出 \(g\) 的块筛,使用杜教筛,\(g\) 的 DGF 为 \(\dfrac{\zeta(x-1)}{\zeta(x-2)}\),故有 \(g*\text{id}_2=\text{id}\),直接套板子即可。

现在要做的就是快速求出 \(h\) 在 Powerful Number 处的点值。由于 \(h\) 是积性函数,只需要考虑 \(h(p^k)\) 的值即可。

由于:

\[f(p^k)=\sum_{i=0}^kh(p^i)g(p^{k-i}) \]

移项:

\[h(p^k)=f(p^k)-\sum_{i=0}^{k-1}h(p^i)g(p^{k-i}) \]

这部分直接计算复杂度大概是 \(\mathcal O(\sqrt n)\) 级别的,不太会证。

求出 \(h(p^k)\) 后 dfs 求 Powerful Number 的时候利用积性直接计算即可。

时间复杂度瓶颈在求出 \(g\) 的块筛,复杂度为杜教筛的 \(\mathcal O(n^{\frac23})\)

顺便学了一下杜教筛不用 unordered_map 的写法,注意到要用到它当且仅当当前的 \(x>n^{\frac 23}\),这时候开另外一个数组,存到 \(\left\lfloor\dfrac nx\right\rfloor\) 下标即可,避免了 unordered_map 的大常数。但还是跑不过 Min25 啊 qwq

小心爆 long long

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e6 + 5, p = 1e9 + 7, inv2 = (p + 1) / 2, inv6 = (p + 1) / 6;
int ans, tot, pri[N], g[N], G[N];
bool vis[N];
ll n;
void sieve(int n) {g[1] = 1;for (int i = 2, k; i <= n; i++) {if (!vis[i]) pri[++tot] = i, g[i] = i - 1;for (int j = 1; j <= tot && i * pri[j] <= n; j++) {vis[k = i * pri[j]] = 1;if (!(i % pri[j])) { g[k] = g[i] * pri[j]; break; }g[k] = g[i] * (pri[j] - 1);}g[i] = (g[i - 1] + (ll)i * g[i]) % p;}
}
int djs(ll x) {if (x < N) return g[x];if (G[n / x]) return G[n / x];int X = x % p, sum = (ll)X * (X + 1) % p * (X * 2 + 1) % p * inv6 % p;for (ll l = 2, r; l <= x; l = r + 1) {r = x / (x / l);sum = (sum - (l + r) % p * (r - l + 1) % p * inv2 % p * djs(x / l) % p + p) % p;}return G[n / x] = sum;
}
void pns(int x, ll m, int h) {if (x > tot || m > n / ((ll)pri[x] * pri[x])) {ll k = n / m;if (k < N) ans = (ans + (ll)h * g[k]) % p;else ans = (ans + (ll)h * G[n / k]) % p;return;}pns(x + 1, m, h);ll k = (ll)pri[x] * pri[x];for (int i = 2; m * k <= n; i++, k *= pri[x]) pns(x + 1, m * k, h * (k % p) % p * (pri[x] - 1) % p * (i - 1) % p);
}
int main() {cin >> n;sieve(N - 1), djs(n), pns(1, 1, 1);cout << ans;
}

DIVCNT3 - Counting Divisors (cube)

题意:求 \(\sum\limits_{i=1}^n\tau(i^3)\)

设积性函数 \(f(p^k)=\tau(p^{3k})=3k+1\),现在要求 \(f\) 的前缀和。使用 Powerful Number 筛,\(f\) 在素数处的取值为 \(4\),构造 \(f=g*h\)。为了好筛,令 \(g=\tau*\tau\),其 DGF 为 \(\zeta^2(x)\),设法求出它的块筛:

\[\sum_{i=1}^ng(i)=\sum_{d=1}^n\tau(d)\sum_{i=1}^{\lfloor\frac nd\rfloor}\tau(i) \]

先求出 \(\tau\) 的块筛,然后整除分块,需要 \(g\)\(n^{\frac23}\) 内的线性筛才能保证复杂度。

\(g(p^k)=\sum\limits_{i=0}^k(i+1)(k-i+1)\),线筛的时候需要把每个 \(i\) 的最小素因子除干净,复杂度仍然是线性的,证明参见 here。(sto syp11 orz)

这样可以在 \(\mathcal O(n^{\frac{2}{3}})\) 的时间复杂度内求出 \(g\) 的块筛。要求出 \(h\) 在 Powerful Number 处的点值,直接使用 PN 筛模板里的做法即可。

由于有多组数据,需要调整阈值。当 \(B=\mathcal O((\sum n)^{\frac 23})\) 时有最优复杂度 \(O((\sum n)^{\frac 23})\)。实际上 SPOJ 上的题面保证了 \(\sum n\le2\times10^{11}\),可以通过。

代码咕咕咕。

Meissel-Lehmer

Meissel-Lehmer 是一种用于快速计算 \(\pi(n)\) 的算法,其中 \(\pi(n)\) 表示 \(n\) 以内的素数个数。

先线性筛出 \(\sqrt n\) 内的素数。然后阈值分治,算出 \(>n^{\frac13}\) 的数中不会被 \(\le n^{\frac13}\) 的素数筛掉的数,设 \(dp_{i,j}\)\(i\) 以内不会被 \(j\) 以内的素数筛掉的数。没有被筛掉的数如果是合数,则一定是两个 \(>n^{\frac13}\) 的素数之积,直接枚举小的素数去计算大素数的方案数即可。有:

\[\pi(n)=\pi(n^{\frac13})+dp_{n,\pi(n^{\frac13})}-1-\sum_{p=n^{\frac13}+1}^{\sqrt n}\left(\pi\left(\dfrac np\right)-\pi(p)+1\right) \]

对于 dp 数组的转移,第 \(j\) 个素数会筛掉所有没有被前面的素数筛掉的 \(p_j\) 的倍数,故 \(dp_{i,j}=dp_{i,j-1}-dp_{\frac{i}{p_j},j-1}\)

dp 的边界条件为 \(dp_{i,0}=i\) 以及 \(dp_{i,j}=\max(\pi(i)-j+1,0)\)(当 \(p_j^2\ge i\) 时)。

递推出一部分 dp 的值,将沿途求出的 \(\pi\) 值存进桶里,这样可以在 \(\mathcal O(n^{\frac23})\) 的复杂度内求出 \(\pi\) 的块筛。

AT_xmascon19_e Sum of f(n)

题意:求 \(\sum\limits_{i=1}^n\Omega(i)\)

对每个素数分别统计答案:

\[\sum_{p\in\mathbb P}^n\sum_{p^k\le n}\left\lfloor\dfrac{n}{p^k}\right\rfloor \]

容易发现对于 \(>\sqrt n\) 的素数其只会产生一次贡献。则对于 \(k>1\),直接枚举范围内的素数即可;对于 \(k=1\),要求:

\[\sum_{p\in\mathbb P}^n\left\lfloor\dfrac{n}{p}\right\rfloor \]

整除分块,使用带记忆化的 Meissel-Lehmer 预处理出 \(\pi\) 的块筛即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, I = 1.8e6, J = 60;
ll n, P[N], ans;
int f[I + 5][J + 5], tot, pri[80005], pi[N];
bool vis[N];
void init() {for (int i = 2; i < N; i++) {if (!vis[i]) pri[++tot] = i;for (int j = 1; j <= tot && i * pri[j] < N; j++) {vis[i * pri[j]] = 1;if (!(i % pri[j])) break;}}for (int i = 1; i < N; i++) pi[i] = pi[i - 1] + !vis[i];for (int i = 1; i <= I; i++) f[i][0] = i;for (int i = 1; i <= I; i++) {for (int j = 1; j <= J; j++) f[i][j] = f[i][j - 1] - f[i / pri[j]][j - 1];}
}
ll dp(ll i, ll j) {if (i <= I && j <= J) return f[i][j];if (!i) return 0; if (!j) return i;if (i < N && (ll)pri[j] * pri[j] >= i) return max(0ll, pi[i] - j);return dp(i, j - 1) - dp(i / pri[j], j - 1);
}
ll Pi(ll x) {if (!x) return 0;if (x < N) return pi[x] - 1;if (P[n / x]) return P[n / x];ll m = pi[(int)cbrt(x)] - 1, ans = dp(x, m) + m - 1; m++;while ((ll)pri[m] * pri[m] <= x) ans -= Pi(x / pri[m]) - m + 1, m++;return P[n / x] = ans;
}
signed main() {cin >> n, init(), Pi(n);for (int i = 1; (ll)pri[i] * pri[i] <= n; i++) {ll m = (ll)pri[i] * pri[i];while (m <= n) ans += n / m, m *= pri[i];}for (ll l = 1, r, k; l <= n; l = r + 1) {r = n / (k = n / l);ans += k * (Pi(r) - Pi(l - 1));}cout << ans;
}

LOJ6181 某个套路求和题

题意:定义 \(f(n)=\prod\limits_{d|n}\mu(d)\),求 \(\sum\limits_{i=1}^nf(i)\)

\(n\) 中有平方因子则 \(f(n)=0\),否则 \(f(n)\) 仅和 \(\omega(n)\) 有关。设 \(k=\omega(n)\),有:

\[f(n)=\prod_{i=0}^k(-1)^i\binom ki=(-1)^{\sum\limits_{i=0}^ki\binom ki}=(-1)^{k2^{k-1}} \]

最后一步使用的组合恒等式可以通过对 \((1+x)^n\) 求导得到。

当且仅当 \(k=1\)\(k2^{k-1}\) 是奇数,此时 \(f(n)=-1\);否则 \(f(n)=1\)

故有:

\[\sum_{i=1}^nf(i)=\sum_{i=1}^n\mu^2(i)-2\pi(n) \]

\(\mu^2\) 的前缀和的方法已经提过很多次了,这里不再赘述。\(\pi(n)\) 直接使用 Meissel-Lehmer 即可。

代码挂了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/881543.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 PyTorch 中理解词向量,将单词转换为有用的向量表示

你要是想构建一个大型语言模型,首先得掌握词向量的概念。幸运的是,这个概念很简单,也是本系列文章的一个完美起点。 那么,假设你有一堆单词,它可以只是一个简单的字符串数组。 animals = ["cat", "dog", "rat", "pig"] 你没法直接…

解决 virsh 无法通过 console 登录虚拟机

报错信息解决方法 登录虚拟机 通过 vnc 或者 ssh登录虚拟机 在虚拟机中执行已执行内容 echo "ttyS0" >> /etc/securetty echo "S0:12345:respawn:/sbin/agetty ttyS0 115200, 1152000 xterm" >>/etc/inittab grub2.cfg grep kernel /etc/grub2…

【分享】Ftrans内外网数据交换方案:打破网络边界,高效融合数据!

随着网络技术和互联网技术的成熟和高速发展,来自互联网的安全威胁越来越严重。数据隔离对很多企业来说并不陌生,越来越多的企业选择网络隔离技术来保护网络安全,而在500强企业中,使用网络隔离技术的企业几乎是绝对性的占比。网络隔离后,仍存在数据交换的需求,内外网数据交…

[gym 102428] Fabricating Sculptures

前言 现在补题是不是不太好 思路 转化题意给定列数 \(S\) , 方块数 \(B\) , 求一种摆放方式, 使得每一列的方块数 \(a_i\) 满足 \(a_i \geq 1\) 且 \(a_i\) 呈非严格单峰观察单峰函数的性质, 发现如果按行处理, 就是单调不增的 更一般的, 每一行放置的方块数非严格小于上一行放…

重做 CF906E Reverses

不是,JJ 怎么退役了,悲。 嗯,先有\[dp_{r}=\min_{l=1}^r[s(l,r)\text{是回文串,且长度不为 2}]dp_{l-1}+1 \] 总复杂度就 \(n^2\),考虑优化 然后有引理说,一个字符串的所有 border 构成 \(\log n\) 个等差数列 我们考虑什么样的点能够转移到 \(dp_i\)尝试借助 \(log\) 段…

Zerto 10.0 Update 6 下载 - 适用于本地、混合和多云环境的灾难恢复和数据保护

Zerto 10.0 Update 6 下载 - 适用于本地、混合和多云环境的灾难恢复和数据保护Zerto 10.0 U6 - 适用于本地、混合和多云环境的灾难恢复和数据保护 勒索软件防护、灾难恢复和多云移动性的统一解决方案 请访问原文链接:https://sysin.org/blog/zerto-10/ 查看最新版。原创作品,…

OpenWrt 24.10 OVF:在 ESXi 8.0、Fusion 13 和 Workstation 17 上运行 OpenWrt 的简单方法

OpenWrt 24.10 OVF:在 ESXi 8.0、Fusion 13 和 Workstation 17 上运行 OpenWrt 的简单方法OpenWrt 24.10 OVF:在 ESXi 8.0、Fusion 13 和 Workstation 17 上运行 OpenWrt 的简单方法 OpenWrt 24.10.0 x86_64 OVF 请访问原文链接:https://sysin.org/blog/openwrt-ovf/ 查看最…

SQL Server 2022新功能:将数据库备份到S3兼容的对象存储

SQL Server 2022新功能:将数据库备份到S3兼容的对象存储 本文介绍将S3兼容的对象存储用作数据库备份目标所需的概念、要求和组件。 数据库备份和恢复功能在概念上类似于使用SQL Server备份到Azure Blob存储的URL作为备份设备类型。 要注意的是,不只是amazon S3对象存储,只要…

护眼神器!LightBulb电脑屏幕护眼软件,你值得拥有!

点击上方蓝字关注我 前言 LightBulb是一个免费的护眼软件,它可以帮助我们在晚上或长时间看电脑屏幕时,减少眼睛的不舒服和疲劳。这个软件会随着一天时间的推移,自动调整电脑屏幕的颜色。比如,在白天,它会让屏幕颜色更偏向冷蓝色,就像阳光下的颜色;到了晚上,它会让屏幕颜…

【JWT安全】攻防指南全面梳理

一、简单介绍 JWT(JSON Web Token)是一种用于身份认证和授权的开放标准,它通过在网络应用间传递被加密的JSON数据来安全地传输信息使得身份验证和授权变得更加简单和安全,JWT对于渗透测试人员而言可能是一种非常吸引人的攻击途径,因为它们不仅是让你获得无限访问权限的关键而…

【CodeForces训练记录】Codeforces Round 1003 (Div. 4)

训练情况赛后反思 题面读的有点疑惑,怀疑自己阅读理解不大行了,简单题狂WA,C2二分调半天没出,水平严重退步 A题 最后两个字母 us 换成 i点击查看代码 #include <bits/stdc++.h> // #define int long long #define endl \nusing namespace std;void solve(){string s;…