目录
前言
参考资料
莫比乌斯反演
预备知识:整数分块(感觉就是一个思想)
- 参考博客:peng-ym-整除分块
总结
- 一般形式: ∑ i = 1 n [ n / i ] \sum_{i=1}^{n}[n/i] ∑i=1n[n/i]
- 过程:r=n/(n/l),然后不断枚举l,求得r,[l,r]部分的值是一样的,不必遍历,直接得到长度*数。
- 复杂度:由 O ( n ) O(n) O(n)变为 O ( n ) O(\sqrt{n}) O(n)
莫比乌斯函数 μ \mu μ(数论函数也可以视作一个数列)
总结
- 定义:很重要
- 性质1:很重要
- 性质2还需要回顾以下欧拉函数才行。
莫比乌斯反演
题目训练
题目1(oi-wiki推荐):求满足gcd(x,y)=k且x,y分别在区间[a,b],[c,d]内的个数(莫比乌斯反演&容斥定理&整数分块)
-
题解:
- 首先由容斥定理把题目简化为求四个 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = 1 ] \sum_{i=1}^{n} \sum_{j=1}^{m}[gcd(i,j)=1] ∑i=1n∑j=1m[gcd(i,j)=1]的形式。
- 其中 [ g c d ( i , j ) = 1 ] [gcd(i,j)=1] [gcd(i,j)=1]表示满足内部条件时返回true,否则返回false。
- 由莫比乌斯函数性质1: ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] ∑d∣nμ(d)=[n=1]
- 带入得到: ∑ i = 1 n ∑ j = 1 m ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{i=1}^{n} \sum_{j=1}^{m}\sum_{d|gcd(i,j)}\mu(d) ∑i=1n∑j=1m∑d∣gcd(i,j)μ(d)(即条件转化为具体求和式子)
- 变形得到: ∑ d = 1 m i n ( n , m ) ∑ i = 1 n [ d ∣ i ] ∑ j = 1 m [ d ∣ j ] \sum_{d=1}^{min(n,m)}\sum_{i=1}^n[d|i]\sum_{j=1}^{m}[d|j] ∑d=1min(n,m)∑i=1n[d∣i]∑j=1m[d∣j]。
- 最后用整数分块解决以下就ok了(不过这里怎么就敢说复杂度为 O ( m a x ( n , m ) O(\sqrt{max(n,m}) O(max(n,m)呢?还是雀食不是根号,但是也大不了多少)。
-
代码:
/*
4. 莫比乌斯反演
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int a, b, c, d, k;
int mu[N], p[N], vis[N];
void Moblus(int n) {
memset(vis, 0, sizeof(vis));
mu[1] = 1;
int tot = 0;
for (int i = 2; i <= n; i++) {
if (!vis[i]) p[tot++] = i, mu[i] = -1;
for (int j = 0; j < tot; j++) {
if (i * p[j] > n) break;
vis[i * p[j]] = 1;
if (i % p[j] == 0) {
mu[i * p[j]] = 0;
break;
} else
mu[i * p[j]] = -mu[i];
}
}
for (int i = 1; i <= n; i++) mu[i] += mu[i - 1];
}
int solve(int n, int m) {
int res = 0;
for (int i = 1, j; i <= min(n, m); i = j + 1) {
j = min(
n / (n / i),
m / (m / i)); // emmm,不敢多问,这你敢说是$O(\sqrt{n})$复杂度?
// 得到一个正确的限制条件,然后模式化(无脑)枚举就好emm
res += (mu[j] - mu[i - 1]) * (n / i) * (m / i);
}
return res;
}
//容斥定理
int f(int x, int k) {
if (x % k == 0) return x / k - 1;
return x / k;
}
signed main() {
Moblus(N - 1);
int T;
cin >> T;
while (T--) {
scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
int ans = solve(b / k, d / k) - solve(b / k, f(c, k)) -
solve(f(a, k), d / k) + solve(f(a, k), f(c, k));
printf("%d\n", ans);
}
return 0;
}