杜教筛学习笔记
前置知识
积性函数
积性函数分为积性函数和完全积性函数。
- 积性函数:\(\forall{a\perp{b}},f(a*b)=f(a)*f(b)\)。常见的有\(\varphi.\mu,\sigma,d\)等
- 完全积性函数:\(\forall{a,b},f(a*b)=f(a)*f(b)\)。常见的有\(\epsilon,I,id\)等
其中\(\epsilon(n)=[n=1],I(n)=1,id(n)=n\)
狄利克雷卷积
\((f*g)(n)=\sum\limits_{d|n}f(d)g(\frac{n}{d})\),满足交换律和结合律。
定义\(\epsilon\)为单位元(\(f*\epsilon=f\),根据定义易得)
\(\begin{cases}\mu*I=\epsilon\\\varphi*I=id\\\mu*id=\varphi\end{cases}\)
莫比乌斯反演
杜教筛
原理
设\(\sum\limits_{i=1}^nf(i)=S(n)\)
再找一个积性函数\(g\),考虑\((f*g)\)的前缀和。
又因为
如果我们能找到合适的\(g\)来快速算出\(\sum\limits_{i=1}^{n}(f*g)(i)\),剩下的就能够整除分块了,从而可以递归求解。
复杂度为\(O(n^{\frac{3}{4}})\),预处理前\(m\)项使复杂度达到\(O(\frac{n}{\sqrt{m}})\),\(m\)取\(n^{\frac{2}{3}}\)时达到最优\(O(n^{\frac{2}{3}})\)
实践
若\(f(n)=\mu(n)\),取\(g(n)=I\),则\((f*g)(n)=\epsilon(n)\)
若\(f(n)=\varphi(n)\),取\(g(n)=I\),则\((f*g)(n)=id(n)\)
注意若\(n=2^{31}-1\)时,整除分块中\(l=r+1\)可能会爆\(int\),改为\(unsigned\ int\)即可。
一定要记得预处理!!同时记得是\(\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)\),而不是\(\sum\limits_{d=2}^n S(d)g(\lfloor\frac{n}{d}\rfloor)\),否则复杂度不对!
下面是模板代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int lim = (1 << 22), mx = 2147483647;
int t, n, tot, mu[4200005], phi[4200005], vis[4200005], pr[1250005], prs_mu[4200005];
ll prs_phi[4200005];
map < int, int > fmu;
map < int, ll > fphi;
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10ll + ch - '0'; ch = getchar();}
return x * fl;
}
void init()
{
phi[1] = mu[1] = 1;
for (int i = 2; i <= lim; i ++ )
{
if (!vis[i])
{
vis[i] = i;
pr[ ++ tot] = i;
phi[i] = i - 1;
mu[i] = -1;
}
for (int j = 1; j <= tot; j ++ )
{
if (i * pr[j] > lim || pr[j] > vis[i]) break;
vis[i * pr[j]] = pr[j];
if (i % pr[j])
{
mu[i * pr[j]] = -mu[i];
phi[i * pr[j]] = phi[i] * phi[pr[j]];
}
else
{
mu[i * pr[j]] = 0;
phi[i * pr[j]] = phi[i] * pr[j];
}
}
}
for (int i = 1; i <= lim; i ++ )
{
prs_mu[i] = prs_mu[i - 1] + mu[i];
prs_phi[i] = prs_phi[i - 1] + phi[i];
}
return;
}
int d_mu(unsigned int x)
{
if (x <= lim) return prs_mu[x];
if (fmu[x]) return fmu[x];
int cnt = 1;
for (unsigned int l = 2, r; l <= x; l = r + 1)
{
r = min(x, x / (x / l));
cnt = cnt - (r - l + 1) * d_mu(x / l);
}
return fmu[x] = cnt;
}
ll d_phi(unsigned int x)
{
if (x <= lim) return prs_phi[x];
if (fphi[x]) return fphi[x];
ll cnt = 1ll * x * (x + 1) / 2;
for (unsigned int l = 2, r; l <= x; l = r + 1)
{
r = min(x, x / (x / l));
cnt = cnt - (r - l + 1) * d_phi(x / l);
}
return fphi[x] = cnt;
}
int main()
{
init();
t = read();
while (t -- )
{
n = read();
printf("%lld %d\n", d_phi(n), d_mu(n));
}
return 0;
}