莫比乌斯反演
莫比乌斯反演
推式子必备知识,通常配合筛积性函数,数论分块
先介绍一下前置知识
狄利克雷卷积
定义:
函数 $h=f * g$
则 $h(n)=\sum_{d|n}f(d)g(\frac n d)=\sum_{ab=n}f(a)g(b)$
单位元:$\varepsilon=[n=1]$
$f * \varepsilon=f$
逆元:如果 $f * g = \varepsilon$,则 $g$ 为 $f$ 的逆元
性质:
-
交换律 $f * g = g * f$
-
结合律 $(f * g) * h = f * (g * h)$
-
分配律 $f * g + h * g = (f+h) * g$
-
两个积性函数的卷积也是积性函数
这可以用于判断想求的函数是否为积性函数
莫比乌斯函数
记作 $\mu$
$$ \mu(x)=\begin{cases} 1,& n = 1\\ 0,& n \text{有平方因子}\\ (-1)^k & n=p_1p_2\cdots p_k\\ \end{cases} $$
性质
是积性函数
$\varphi * 1= id$
$\sum_{d|n}\mu(d)=[n=1]$
即 $\mu * 1=\varepsilon$
在狄利克雷卷积中,$\mu$ 为 $1$ 的逆元
所以,
$$[\gcd(i,j)=1]=\varepsilon(\gcd(i,j))=\sum_{d|\gcd(i,j)}\mu(d)$$
这是推式子最常用的结论之一
莫比乌斯变换
形式一:$f(n)=\sum_{d|n}g(d)$,则 $g(n)=\sum_{d|n}\mu(d)f(\frac n d)$
形式二:$f(n)=\sum_{n|d}g(d)$,则 $g(n)=\sum_{n|d}\mu(\frac n d)f(d)$
应用
线性筛法
利用它是积性函数和定义
mu[1] = 1;
for(ll i = 2; i <= mx; ++i)
{
if(!st[i]) prime[++cnt] = i, mu[i] = -1;
for(ll j = 1; j <= cnt && i * prime[j] <= mx; ++j)
{
st[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
常见推式子技巧
化成关于 $\gcd$ 的形式,枚举 $\gcd$ 的值
就转换成 $[\gcd(i,j)=1]$ 的式子
然后用结论,推为 $\sum_{d|\gcd(i,j)}\mu(d)$
交换求和顺序,先枚举 $d$,则 $i,j$ 均为 $d$ 的倍数
然后推成 $\lfloor\frac i {dt}\rfloor\lfloor\frac j {dt}\rfloor$
如果需要的话,接下来,令 $T=dt$,枚举 $T$
总之,先可能使 $\sum$ 变多,但最终想让 $\sum$ 变少,并化成可以预处理的形式
P2522 [HAOI2011] Problem b
如果有下界,推式子就有很多麻烦
所以用容斥,求四遍就行
$$ \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k] =\sum_{i=1}^{\lfloor\frac n k\rfloor}\sum_{j=1}^{\lfloor\frac m k\rfloor} [\gcd(i,j)=1] $$$$ =\sum_{i=1}^{\lfloor\frac n k\rfloor}\sum_{j=1}^{\lfloor\frac m k\rfloor} \sum_{d|\gcd(i,j)}\mu(d)=\sum_{d}^{\min(\lfloor\frac n k\rfloor,\lfloor\frac m k\rfloor)}\mu(d)\sum_{i=1}^{\lfloor\frac n k\rfloor}[d|i]\sum_{j=1}^{\lfloor\frac m k\rfloor}[d|j] $$
$$ =\sum_{d}^{\min(\lfloor\frac n k\rfloor,\lfloor\frac m k\rfloor)}\mu(d)\lfloor\frac n {kd}\rfloor\lfloor\frac m {kd}\rfloor $$
这个式子就可以整除分块了,复杂度 $O(n\sqrt k)$
ll solve(ll n, ll m)
{
n /= k, m /= k;
if(!n || !m) return 0;
ll res = 0;
for(ll l = 1, r = 1; l <= n && l <= m; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
res += (qzh[r] - qzh[l - 1]) * (n / l) * (m / l);
}
return res;
}
int main()
{
mu[1] = 1;
for(ll i = 2; i <= mx; ++i)
{
if(!st[i]) prime[++cnt] = i, mu[i] = -1;
for(ll j = 1; j <= cnt && i * prime[j] <= mx; ++j)
{
st[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
mu[i * prime[j]] = -mu[i];
}
}
for(ll i = 1; i <= mx; ++i) qzh[i] = qzh[i - 1] + mu[i];
q = read();
while(q--)
{
a = read(), b = read(), c = read(), d = read(), k = read();
print(solve(b, d) - solve(a - 1, d) - solve(b, c - 1) + solve(a - 1, c - 1)), putchar('\n');
}
return 0;
}
P4449 于神之怒加强版
直接开始推
$$ \sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k=\sum_d d^k\sum_{i=1}^{\lfloor\frac n d\rfloor}\sum_{j=1}^{\lfloor\frac m d\rfloor}[\gcd(i,j)=1] $$$$ =\sum_d d^k\sum_{i=1}^{\lfloor\frac n d\rfloor}\sum_{j=1}^{\lfloor\frac m d\rfloor}\sum_{t|\gcd(i,j)}\mu(t) $$
后面一坨跟上一题很像,直接就化简为
$$ \sum_d d^k\sum_{t}^{\min(\lfloor\frac n d\rfloor,\lfloor\frac m d\rfloor)}\mu(t)\lfloor\frac n {dt}\rfloor\lfloor\frac m {dt}\rfloor $$
但如果枚举 $d$,对 $t$ 整除分块,复杂度仍不够
令 $T=dt$,交换求和顺序,则
$$ =\sum_{T}\lfloor\frac n {T}\rfloor\lfloor\frac m {T}\rfloor\sum_{d|T}d^k \mu(\frac T d)$$
令 $f(x)=x^k$,则 $f(x)$ 为积性函数,发现 $g(T)=\sum_{d|T}f(d) \mu(\frac T d)$ 是卷积的形式
由卷积的性质得 $g(x)$ 也是积性函数,可以用欧拉筛预处理
具体的,
$$ g(n)=\begin{cases} n^k-1,& n \text{为质数}\\ n^k-(\frac n p)^k,&n=p^x,p\text{为质数}\\ \end{cases} $$
这样同时记录每个数最小的质因子的乘积,即可利用积性函数 $O(n)$ 求 $g$
对 $T$ 整除分块,复杂度 $O(T\sqrt n)$
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7, N = 5000010, mx = 5000000, SZ = (1 << 14) + 5;
ll t, k, n, m, st[N], prime[N], ans, cnt, mn[N], f[N], g[N], low[N];
static char buf[SZ], *bgn = buf, *til = buf;
char getc()
{
if(bgn == til) bgn = buf, til = buf + fread(buf, 1, SZ, stdin);
return (bgn == til) ? EOF : *bgn++;
}
ll read()
{
char ch = getc(); ll x = 0;
while(ch < '0' || ch > '9') ch = getc();
while(ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + ch - '0', ch = getc();
return x;
}
void print(ll x)
{
if(x / 10) print(x / 10);
putchar(x % 10 + '0');
}
ll add(ll a, ll b) {return (a + b >= mod) ? (a + b - mod) : (a + b);}
ll qmi(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
b >>= 1, a = a * a % mod;
}
return res;
}
int main()
{
t = read(), k = read();
f[1] = g[1] = low[1] = st[1] = 1;
for(ll i = 2; i <= mx; ++i)
{
if(!st[i]) prime[++cnt] = i, f[i] = qmi(i, k), low[i] = i, g[i] = add(f[i], mod - 1);
for(ll j = 1; j <= cnt && i * prime[j] <= mx; ++j)
{
st[i * prime[j]] = 1, f[i * prime[j]] = f[i] * f[prime[j]] % mod;
if(i % prime[j] == 0)
{
low[i * prime[j]] = low[i] * prime[j];
if(low[i * prime[j]] == i * prime[j]) g[i * prime[j]] = add(f[i * prime[j]], mod - f[i]);
else g[i * prime[j]] = g[i / low[i]] * g[low[i] * prime[j]] % mod;
break;
}
g[i * prime[j]] = g[i] * g[prime[j]] % mod, low[i * prime[j]] = prime[j];
}
}
// for(ll i = 1; i <= 20; ++i) cerr << i << ": " << f[i] << " " << g[i] << " " << low[i] << "\n";
for(ll i = 2; i <= mx; ++i) g[i] = add(g[i], g[i - 1]);
while(t--)
{
n = read(), m = read(), ans = 0;
for(ll l = 1, r = 1; l <= n && l <= m; l = r + 1)
{
r = min(n / (n / l), m / (m / l));
ans = add(ans, (n / l) * (m / l) % mod * add(g[r], mod - g[l - 1]) % mod);
}
print(ans), putchar('\n');
}
return 0;
}