莫比乌斯反演学习笔记
前置知识
-
莫比乌斯函数\(\mu(d)\):当\(𝑑=1\)时,\(𝜇(𝑑)=1\);当\(𝑑=\prod\limits_{i=1}^𝑘p_𝑖\)且\(𝑝_𝑖\)为互异素数时,\(𝜇(𝑑)=(−1)^𝑘\)。
-
莫比乌斯函数性质:
\(1.\)对于任意正整数\(𝑛\),\(\sum\limits_{𝑑|𝑛}\mu(𝑑)=[𝑛=1]\)。
\(2.\)对于任意正整数\(𝑛\),\(\sum\limits_{𝑑|𝑛}\frac{\mu(𝑑)}{𝑑}=\frac{\varphi(𝑛)}{n}\)。
内容
第一种形式:已知\(𝐹(𝑥)=\sum\limits_{𝑑|𝑥}𝑓(𝑑)\),则\(𝑓(𝑥)=\sum\limits_{𝑑|𝑥}\mu(\frac{x}{d})𝐹(𝑑)\)
第二种形式:已知\(𝐹(𝑥)=\sum\limits_{x|d}^{n}𝑓(𝑑)\),则\(𝑓(𝑥)=\sum\limits_{x|d}^{n}\mu(\frac{d}{x})𝐹(𝑑)\)
题目
[Luogu] P3455 [POI2007]ZAP-Queries
Description
\(n\)组询问,给出\(a,b,d\),求满足\(1 \leq x \leq a\),\(1 \leq y \leq b\),且\(\gcd(x,y)=d\)的二元组\((x,y)\)的数量。\((1≤n≤5×10^4,1 \leq d \leq a,b \leq 5 \times 10^4)\)
Solution
不妨设\(a\le{b}\),那么答案为
我们令\(x=x*d,y=y*d\),那么答案为
我们再令\(x=x*k,y=y*k\),那么答案为
用整除分块可以做到\(O(n\sqrt{a})\)。
Code
#include <bits/stdc++.h>
using namespace std;
int t, x, y, d, tot, vis[50005], pr[12505], mu[50005], sum[50005];
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 << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
int main()
{
mu[1] = 1;
for (int i = 2; i <= 50000; i ++ )
{
if (!vis[i])
{
vis[i] = i;
pr[ ++ tot] = i;
mu[i] = -1;
}
for (int j = 1; j <= tot; j ++ )
{
if (i * pr[j] > 50000 || pr[j] > vis[i]) break;
vis[i * pr[j]] = pr[j];
if (i % pr[j]) mu[i * pr[j]] = -mu[i];
else mu[i * pr[j]] = 0;
}
}
for (int i = 1; i <= 50000; i ++ )
sum[i] = sum[i - 1] + mu[i];
t = read();
while (t -- )
{
x = read(), y = read(), d = read();
x /= d; y /= d;
int mn = min(x, y), res = 0;
for (int l = 1, r; l <= mn; l = r + 1)
{
r = min(min(x / (x / l), y / (y / l)), mn);
res = res + (sum[r] - sum[l - 1]) * (x / l) * (y / l);
}
printf("%d\n", res);
}
return 0;
}
[Luogu] P2257 YY的GCD
Description
\(T\)组询问,给定\(n,m\),求\(1 \leq x \leq n,1 \leq y \leq m\)且\(\gcd(x, y)\)为质数的\((x, y)\)有多少对。\(T=10^4,n,m\le{10^7}\)
Solution
不妨设\(n\le{m}\),那么答案为
我们令\(x=x*p,y=y*p\),那么答案为
我们再令\(x=x*k,y=y*k\),那么答案为
接下来好像有点不太好弄,我们改为枚举\(S=kp\),用\(S\)来表示\(k\)。
那么答案为
这样子是为了埃氏筛求出后面的部分。具体地,我们枚举所有质数,对于它的倍数,加上对应贡献。
用整除分块加预处理,可以做到\(O(n\ln(n)+T\sqrt{n})\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int t, n, m, tot, vis[10000005], pr[2500005], mu[100000005], cnt[10000005];
ll sum[10000005];
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 << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
int main()
{
mu[1] = 1;
for (int i = 2; i <= 10000000; i ++ )
{
if (!vis[i])
{
vis[i] = i;
pr[ ++ tot] = i;
mu[i] = -1;
}
for (int j = 1; j <= tot; j ++ )
{
if (i * pr[j] > 10000000 || pr[j] > vis[i]) break;
vis[i * pr[j]] = pr[j];
if (i % pr[j]) mu[i * pr[j]] = -mu[i];
else mu[i * pr[j]] = 0;
}
}
for (int i = 1; i <= tot; i ++ )
for (int j = pr[i]; j <= 10000000; j += pr[i])
cnt[j] += mu[j / pr[i]];
for (int i = 1; i <= 10000000; i ++ )
sum[i] = sum[i - 1] + (ll)cnt[i];
t = read();
while (t -- )
{
n = read(), m = read();
int mn = min(n, m); ll res = 0ll;
for (int l = 1, r; l <= mn; l = r + 1)
{
r = min(min(n / (n / l), m / (m / l)), mn);
res = res + 1ll * (sum[r] - sum[l - 1]) * (n / l) * (m / l);
}
printf("%lld\n", res);
}
return 0;
}