莫比乌斯反演学习笔记
前言
本文只是作者学习莫比乌斯反演后做的相关笔记,可能并不适合入门。
基础
莫比乌斯函数
设 \(x=p_1^{\alpha_1}\times p_2^{\alpha_2}\times \dots\times p_k^{\alpha_k}\)。
则:
\(\mu(x)=\begin{cases} 1&{x=1} \\ 0&{\alpha_i>1(1\le i\le k)} \\ (-1)^k &\text{otherwise} \end{cases}\)
一个基本性质
设 \(S(n)=\sum\limits_{d|n}\mu(d)\)。
则 \(S(n)=[n=1]\)。
证明略。
一个简单结论
第一个式子
证明:
第二个式子
证明:
令 \(d'=\frac{d}{n}\)。
值得注意的是,我们在做题中常用的是第二个式子。
例题
题意:求 \(\sum\limits_{a\le x\le b}\sum\limits_{c\le y\le d}[\gcd(x, y)=k]\)。
设 \(S(a, b)=\sum\limits_{1\le x\le a}\sum\limits_{1\le y\le b}[\gcd(x, y)=k]\)。
可以通过二维前缀和的方式转化问题,答案即为 \(S(b,d)-S(a-1,d)-S(b,c-1)+S(a-1,c-1)\)。
接下来有两种方法求解。
方法一:
设 \(n=\lfloor\frac{a}{k}\rfloor\),\(m=\lfloor\frac{b}{k}\rfloor\)。
方法二:
设 \(F(n)=\sum\limits_{x=1}^a\sum\limits_{y=1}^b[n|\gcd(x,y)]=\lfloor\frac{a}{n}\rfloor\lfloor\frac{b}{n}\rfloor\),\(f(n)=\sum\limits_{x=1}^a\sum\limits_{y=1}^b[\gcd(x,y)=n]\)。
很显然有:\(F(n)=\sum\limits_{n|d}f(d)\)。
莫比乌斯反演得到:\(f(n)=\sum\limits_{n|d}\mu(\frac{d}{n})F(d)\)。
令 \(d'=\frac{d}{n}\),\(a'=\frac{a}{n}\),\(b'=\frac{b}{n}\)。
推式子:
然后整除分块即可。
代码:
#include <bits/stdc++.h>
#define DEBUG fprintf(stderr, "Passing [%s] line %d\n", __FUNCTION__, __LINE__)
#define File(x) freopen(x".in","r",stdin); freopen(x".out","w",stdout)
using namespace std;
typedef long long LL;
typedef pair <int, int> PII;
typedef pair <int, PII> PIII;
template <typename T>
inline T gi()
{
T f = 1, x = 0; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return f * x;
}
const int INF = 0x3f3f3f3f, N = 50003, M = N << 1;
int T;
int mu[N], sum[N];
bool isprime[N];
int pri[N], tot;
inline void pre()
{
mu[1] = 1;
for (int i = 2; i <= 50000; i+=1)
{
if (!isprime[i]) pri[++tot] = i, mu[i] = -1;
for (int j = 1; j <= tot && 1ll * i * pri[j] <= 50000; j+=1)
{
isprime[i * pri[j]] = true;
if (i % pri[j] == 0) break;
mu[i * pri[j]] = -mu[i];
}
}
for (int i = 1; i <= 50000; i+=1) sum[i] = sum[i - 1] + mu[i];
}
inline LL S(int a, int b, int k)
{
a /= k, b /= k;
int n = min(a, b);
LL ans = 0;
for (int l = 1, r; l <= n; l = r + 1)
{
r = min(n, min(a / (a / l), b / (b / l)));
ans = ans + 1ll * (sum[r] - sum[l - 1]) * (a / l) * (b / l);
}
return ans;
}
int main()
{
//File("");
pre();
T = gi <int> ();
while (T--)
{
int a = gi <int> (), b = gi <int> (),
c = gi <int> (), d = gi <int> (),
k = gi <int> ();
printf("%lld\n", S(b, d, k) - S(a - 1, d, k)
- S(b, c - 1, k) + S(a - 1, c - 1, k));
}
return 0;
}