前置知识
狄利克雷卷积
形如 f(n)=d∣n∑g(d)∗h(dn),记作 f是 g 和 h的狄利克雷卷积,记作 f=g∗h。
eg. f(n)=d∣n∑g(d)⇒f=g∗1
f=f∗e, e 为单位函数
狄利克雷卷积的性质
狄利克雷卷积满***换律和结合律,即
f=g∗h∗k=g∗k∗h=g∗(h∗k)
进入正题
莫比乌斯函数
定义
定义可以参考其他 blog (TAT,分段函数实在太难打了)
性质
d∣n∑μ(d)=[n=1],即是 μ∗1=e(n), e 为单位函数。
这是莫比乌斯函数很重要的性质,反演基本基于此。
证明如下:
考虑到每个 u(d) 对 ans 贡献只是 1 或 −1( 0 不用管)
设 n=i=1∏mpiai
那么 n>1 时 ans=k=0∑m(−1)kCmk=(1−1)m=0
n=1时, ans=1。
证毕。
莫比乌斯反演定理
若有 f(n)=d∣n∑g(d),则有 g(n)=d∣n∑f(d)∗μ(dn)
证明如下:
现有 f=g∗1,则有 f∗μ=g∗(μ∗1)=g∗e=g(这是狄利克雷卷积的证法,当然还有其他证法,可参考其他 blog)
莫比乌斯函数的应用
①:让我们先来考虑下面一道最最最经典的题:
求 ans=i=1∑nj=1∑m[gcd(i,j)==1](n<m)
解:考虑到 [gcd(i,j)==1] 是单位函数, 而 e=μ∗1,于是
ans=i=1∑nj=1∑md∣gcd(i,j)∑μ(d)=i=1∑nj=1∑md∣i,d∣j∑μ(d)
考虑每个 μ(d) 出现了多少次,则有
ans=d=1∑nμ(d)∗⌊dn⌋∗⌊dm⌋,然后用除法分块,可以使复杂度达到 O(n )
②:进阶版
求 ans=i=1∑nj=1∑mgcd(i,j)(n<m)
枚举 d=gcd(i,j),有
ans=d=1∑ndi=1∑nj=1∑m[gcd(i/d,j/d)==1]=d=1∑ndi=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)==1]=d=1∑ndi=1∑⌊dn⌋j=1∑⌊dm⌋k∣i,k∣j∑μ(k)=d=1∑ndk=1∑⌊dn⌋μ(k)⌊kdn⌋⌊kdm⌋,然后 ⌊dn⌋分块, ⌊kdn⌋分块。
复杂度是 O(n ∗n =n)
③:再进阶一点点
这题是 bzoj 2154 Crash的数字表格 / JZPTAB
求 ans=i=1∑nj=1∑mlcm(i,j)
众所周知, lcm(i,j)=gcd(i,j)i∗j,于是
ans=i=1∑nj=1∑mgcd(i,j)i∗j=d=1∑ni=1∑nj=1∑mdi∗j∗[gcd(i,j)==d]=d=1∑ni=1∑⌊dn⌋j=1∑⌊dm⌋ijd[gcd(i,j)==1]=d=1∑ndi=1∑⌊dn⌋j=1∑⌊dm⌋ijk∣i,k∣j∑μ(k),然后考虑枚举 k,有
ans=d=1∑ndk=1∑⌊dn⌋μ(k)∗k2i=1∑⌊kdn⌋ij=1∑⌊kdm⌋j,然后两个整除分块即可,复杂度 O(n)
代码如下
#include <bits/stdc++.h>
#define N 10000005
#define LL long long
#define mod 20101009
using namespace std;
int p[N], cnt, mu[N], s[N], g[N];
bool x[N];
LL z = 1, t;
int main(){
int i, j, n, m, l1, l2, r1, r2, sum, ans, a, b;
scanf("%d%d", &n, &m);
if(n > m) swap(n, m);
mu[1] = 1;
for(i = 2; i <= m; i++){
if(!x[i]) x[i] = 1, p[++cnt] = i, mu[i] = -1;
for(j = 1; j <= cnt; j++){
t = z * i * p[j];
if(t > m) break;
x[t] = 1;
if(i % p[j] == 0) break;
mu[t] = -mu[i];
}
}
for(i = 1; i <= m; i++) g[i] = (g[i - 1] + i) % mod, s[i] = (s[i - 1] + z * mu[i] * i * i % mod + mod) % mod;
ans = sum = 0;
for(l1 = 1; l1 <= n; l1 = r1 + 1){
j = n / l1; r1 = min(n / (n / l1), m / (m / l1));
a = n / l1, b = m / l1;
sum = 0;
for(l2 = 1; l2 <= j; l2 = r2 + 1){
r2 = min(a / (a / l2), b / (b / l2));
sum = (sum + z * (s[r2] - s[l2 - 1]) * g[a / l2] % mod * g[b / l2] % mod) % mod;
}
ans = (ans + z * sum * (g[r1] - g[l1 - 1]) % mod) % mod;
}
printf("%d", (ans + mod) % mod);
return 0;
}
④ 再进阶一点点
求 ans=i=1∑nj=1∑m[gcd(i,j)∈prime](n<m)
考虑枚举 n 以内的素数,于是有
ans=p∑i=1∑nj=1∑m[gcd(i,j)==p]=p∑i=1∑⌊pn⌋j=1∑⌊pm⌋[gcd(i,j)==1]=p∑i=1∑⌊pn⌋j=1∑⌊pm⌋k∣i,k∣j∑μ(k)=p∑k=1∑⌊pn⌋μ(k)⌊kpn⌋⌊kpm⌋
操作来了!!
我们考虑枚举 T=kp,于是有
ans=T=1∑n⌊Tn⌋⌊Tm⌋p∣T∑μ(pT)
对于后面那部分,我们可以预处理,将每个素数的倍数 i 加上 μ(i),复杂度是 O(wln(w)), w 为素数个数,而 w 大约是 ln(n)n,所以总的复杂度是 O(n),单次询问的复杂度为 O(n )
暂时就写这么多吧