莫比乌斯反演
狄利克雷卷积
f, g 为数论函数
\(h=f*g\), 即 \(h(n)=\sum\limits_{d_1*d2=n}f(d_1)*g(d_2)\)
性质
-
满足交换律
-
满足结合律,即 \(p(n)=(f*g)*h=f*(g*h)=\sum\limits_{d_1*d_2*d_3=n}f(d_1)*g(d_2)*h(d_3)\)
-
若 \(f,g\) 是积性函数,则 \(f*g\) 是积性函数(完全积性函数不满足此条性质)
常见卷积:
- \(1*\mu=e\)
- \(f*e=f\)
- \(id*\mu=\phi\)
莫比乌斯反演
\(f=g*1 \iff g=f*\mu\)
即 \(f(n)=\sum\limits_{d\mid n}g(d)\iff g(n)=\sum\limits_{d\mid n} f(d)*\mu(\frac nd)\)
证明:
引理1:\(1*\mu=e\)
引理2:\(f*e=f\)
-
若 \(f=g*1\) , 两边同时卷上 \(\mu\)
\(f*\mu=g*1*\mu=g*(1*\mu)=g*e=g\)
-
若 \(g=f*\mu\), 两边同时卷上 \(1\)
\(g*1=f*\mu*1=f*(\mu*1)=f*e=f\)
引理1,2 暴力带入狄利克雷卷积并利用积性函数的性质(两个积性函数卷起来仍是积性函数)即可证明
莫比乌斯反演 - 题目 - Daimayuan Online Judge
\(f=g*1\), 因此 \(g=f*\mu\), 线性筛求出 \(\mu\)
求 \(g(i)\) 的过程可考虑每个因子对 \(g(i)\) 的贡献,因此可以:
for (int d1 = 1; d1 <= n; d1++)
for (int d2 = 1; d1 *d2 <= n; d2++)
g[d1*d2] += f[d1] * mu[d2];
由调和级数,复杂度为 \(O(nlogn)\)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned int uint;
const int N = 1e6 + 10;
uint n;
uint pr[N/5], p[N], cnt, mu[N], f[N], g[N];
unsigned int A,B,C;
inline unsigned int rng61()
{
A ^= A << 16;
A ^= A >> 5;
A ^= A << 1;
unsigned int t = A;
A = B;
B = C;
C ^= t ^ A;
return C;
}
void get_primes(uint n)
{
p[1] = 1, mu[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!p[i])
{
p[i] = i;
mu[i] = -1;
pr[++cnt] = i;
}
for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
{
p[i*pr[j]] = pr[j];
if (p[i] == pr[j])
{
mu[i*pr[j]] = 0;
break;
}
mu[i*pr[j]] = -mu[i];
}
}
}
int main()
{
scanf("%d%u%u%u", &n, &A, &B, &C);
for (int i = 1; i <= n; i++)
f[i] = rng61();
get_primes(n);
for (int d1 = 1; d1 <= n; d1++)
for (int d2 = 1; d1 *d2 <= n; d2++)
g[d1*d2] += f[d1] * mu[d2];
uint ans = 0;
for (int i = 1; i <= n; i++) ans ^= g[i];
printf("%u\n", ans);
return 0;
}
互质数对 - 题目 - Daimayuan Online Judge
\(T\;(1<=T<=1000)\) 组询问,每次给出 \(n,m\;(1<=n,m<=10^7)\), 求 满足 \(1<=i<=n,\;1<=j<=m\) 的 \(\gcd(i,j)=1\) 的对数
因此求出 \(\mu\) 的前缀和,加上整除分块即可
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int pr[N/5], p[N], cnt, mu[N], s[N];
void get_primes(int n)
{
p[1] = mu[1] = s[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!p[i])
pr[++cnt] = i, p[i] = i, mu[i] = -1;
for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
{
p[i*pr[j]] = pr[j];
if (p[i] == pr[j])
{
mu[i*pr[j]] = 0;
break;
}
mu[i*pr[j]] = -mu[i];
}
}
for (int i = 2; i <= n; i++)
s[i] = s[i-1] + mu[i];
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
get_primes(N - 10);
while(T--)
{
int n, m;
cin >> n >> m;
if (n > m) swap(n, m);
ll ans = 0;
for (int l = 1; l <= n; l++)
{
int r = min(n / (n / l), m / (m / l));
ans += (ll)(s[r] - s[l-1]) * (n / l) * (m / l);
l = r;
}
cout << ans << endl;
}
return 0;
}
gcd之和 - 题目 - Daimayuan Online Judge
跟上一题类似
形如: \(\sum_{i=1}^n\sum_{j=1}^mf(\gcd(i,j))\) ,可用 \(g=f*\mu\) 找到 \(g\) ,再用 \(f=g*1\) 带入得
\(\sum_{i=1}^n\sum_{j=1}^m\sum\limits_{d\mid \gcd(i,j)}g(d)\) = \(\sum_{d=1}^ng(d)*\lfloor\frac ni\rfloor * \lfloor \frac mj \rfloor\)
本题中 \(g=id*\mu=\phi\)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int pr[N/5], p[N], cnt, phi[N];
ll s[N];
void get_primes(int n)
{
p[1] = phi[1] = s[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!p[i])
pr[++cnt] = i, p[i] = i, phi[i] = i - 1;
for (int j = 1; j <= cnt && pr[j] <= n / i; j++)
{
p[i*pr[j]] = pr[j];
if (p[i] == pr[j])
{
phi[i*pr[j]] = phi[i] * pr[j];
break;
}
phi[i*pr[j]] = phi[i] * (pr[j] - 1);
}
}
for (int i = 2; i <= n; i++)
s[i] = s[i-1] + phi[i];
}
int main()
{
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
get_primes(N - 10);
while(T--)
{
int n, m;
cin >> n >> m;
if (n > m) swap(n, m);
ll ans = 0;
for (int l = 1; l <= n; l++)
{
int r = min(n / (n / l), m / (m / l));
ans += (ll)(s[r] - s[l-1]) * (n / l) * (m / l);
l = r;
}
cout << ans << endl;
}
return 0;
}