莫比乌斯反演

做~自己de王妃 提交于 2021-02-13 05:31:23

整除分块:

好吧这就是一个前置小技巧,来看这样一个式子$$\sum_{i=1}^{n} \lfloor \frac{n}{i} \rfloor$$ 利用整除分块的方法,我们可以在$O(\sqrt(n))$的时间内计算出这个式子

容易发现,有许多$\lfloor \frac{n}{i} \rfloor$的值是一样的,而且它们都呈块状分布,经过一些小计算后可以发现,每一块的右边界即为$n/(n/l)$,于是我们就可以快速计算了

1 for(int l = 1, r; l <= n; l = r + 1)
2 {
3     r = n / (n / l);
4     ans += (r - l + 1) * (n / l);
5 }

莫比乌斯函数:

来看一些有趣的性质:

  1. 对于任意正整数$n$,$\sum_{d|n} \mu(d) = [n = 1]$
  2. 对于任意正整数$n$,$\sum_{d|n} \frac{\mu(d)}{d} = \frac{\phi(n)}{n}$

基于线性筛素数的方法,我们可以写出线性筛莫比乌斯函数的代码:

 1 void get_mu(int n)
 2 {
 3     mu[1] = 1;
 4     for(int i = 2; i <= n; ++i)
 5     {
 6         if(!vis[i]) {prim[++cnt] = i; mu[i] = -1;}
 7         for(int j = 1; j <= cnt && prim[j] * i <= n; ++j)
 8         {
 9             vis[prim[j] * i] = 1;
10             if(i % prim[j] == 0) break;
11             mu[i * prim[j]] = -mu[i];
12         }
13     }    
14 }

莫比乌斯反演:

  •  定理:$F(n)$和$f(n)$是定义在非负整数集合上的两个函数,并且满足条件:$$F(n) = \sum_{d|n} f(d)$$

   那么我们有结论:$$f(n) = \sum_{d|n} \mu(d) F(\lfloor \frac{n}{d} \rfloor)$$

   这就是所谓的莫比乌斯反演定理

  • 证明:$$f(n) = \sum_{d|n} \mu(d) F(\lfloor \frac{n}{d} \rfloor) = \sum_{d|n} \mu(d) \sum_{k|\frac{n}{d}}f(k) = \sum_{k|n}f(k) \sum_{d|\frac{n}{k}} \mu(d)$$

     由上面的性质1可知,当且仅当$\frac{n}{k} = 1$时,即$k = n$时,$\sum_{d|\frac{n}{k}} \mu(d) = 1$,其余时候都为0,由此可知定理成立

  • 莫比乌斯反演还有另外一种常用的形式,若满足条件:$$F(n) = \sum_{n|d}f(d)$$

   有结论:$$f(n) = \sum_{n|d} \mu(\frac{d}{n}) F(d)$$


例题:

     设小于$min(n, m)$的质数为$p_1, p_2...p_k$,题目所求即为:

   $$Ans = \sum_{x = 1}^{n} \sum_{y = 1}^{m} \sum_{i = 1}^{k} [gcd(x, y) = p_i]$$

   设$f(d)$表示满足$gcd(x, y) = d$且$1≤x≤n, 1≤y≤m$的$(x, y)$的对数

   设$F(d)$表示满足$d|gcd(x, y)$且$1≤x≤n, 1≤y≤m$的$(x, y)$的对数

   显然有:

   $$F(x) = \sum_{x|d}^{\min(n, m)} f(d) = \lfloor \frac{n}{x} \rfloor \lfloor \frac{m}{x} \rfloor$$

   反演一下可得:

   $$f(x) = \sum_{x|d}^{\min(n, m)} \mu(\frac{d}{x}) F(d)$$

   我们所求的答案即为:

   $$Ans = \sum_{i = 1}^{k} f(p_i) = \sum_{i = 1}^{k} \sum_{p_i|d}^{\min(n, m)} \mu(\frac{d}{p_i}) F(d)$$

   观察上式,发现$p_i$的倍数只有$\lfloor \frac{\min(n, m)}{p_i} \rfloor$个,因此我们更改枚举的对象为$p_i$的系数:

   $$Ans = \sum_{i = 1}^{k} \sum_{d = 1}^{\lfloor \frac{\min(n, m)}{p_i} \rfloor} \mu(d) F(dp_i) = \sum_{i = 1}^{k} \sum_{d = 1}^{\lfloor \frac{\min(n, m)}{p_i} \rfloor} \mu(d) \lfloor \frac{n}{dp_i} \rfloor \lfloor \frac{m}{dp_i} \rfloor$$

   令$T = dp_i$,枚举一下$T$,提到前面去,即:

   $$Ans = \sum_{T = 1}^{\min(n, m)} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \sum_{k|T, k∈prime} \mu(\frac{T}{k})$$

   利用整除分块的技巧,我们首先用线性筛预处理出$\sum_{k|T, k∈prime} \mu(\frac{T}{k})$的前缀和,然后把取值相同的$\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor$放在一起计算

 1 // luogu-judger-enable-o2
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 
 5 const int MAXN = 10000010;
 6 
 7 int cnt, prime[MAXN], mu[MAXN], g[MAXN];
 8 bool vis[MAXN]; long long sum[MAXN];
 9 
10 void get_mu(int n)
11 {
12     mu[1] = 1;
13     for(int i = 2; i <= n; ++i)
14     {
15         if(!vis[i]) {prime[++cnt] = i; mu[i] = -1;}
16         for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
17         {
18             vis[i * prime[j]] = 1;
19             if(i % prime[j] == 0) break;
20             mu[i * prime[j]] = -mu[i];
21         }
22     }
23     for(int j = 1; j <= cnt; ++j)
24         for(int i = 1; i * prime[j] <= n; ++i)
25             g[i * prime[j]] += mu[i];
26     for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + g[i];
27 }
28 
29 int T, n, m;
30 long long ans;
31 
32 int main()
33 {
34     get_mu(MAXN - 10);
35     scanf("%d", &T);
36     while(T--)
37     {
38         scanf("%d %d", &n, &m);
39         if(n > m) swap(n, m); ans = 0;
40         for(int l = 1, r; l <= n; l = r + 1)
41         {
42             r = min(n / (n / l), m / (m / l));
43             ans += (long long)(n / l) * (m / l) * (sum[r] - sum[l - 1]);
44         }
45         printf("%lld\n", ans);
46     }
47     return 0;
48 }

 

   有一个结论:$d(i,j) = \sum_{x|i} \sum_{y|j} [gcd(x, y) = 1]$,证明留给读者作习题(我也不会

   那么题目就是求$$\sum_{i = 1}^{n} \sum_{j = 1}^{m} \sum_{x|i} \sum_{y|j} [gcd(x, y) = 1]$$

   把枚举对象换一下($i$换成$x$,$j$换成$y$)就是:$$\sum_{i = 1}^{n} \sum_{j = 1}^{m} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor [gcd(i, j) = 1]$$

   看上去可以反演了,我们设:

   $$f(x) = \sum_{i = 1}^{n} \sum_{j = 1}^{m} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor [gcd(i, j) = x]$$

   $$F(x) = \sum_{x|d} f(d)$$

   我们所求的答案即为:

   $$Ans = f(1) = \sum_{d = 1}^{min(n, m)} \mu(d) F(d)$$

   现在来看$F(x)$:

   $$F(x) = \sum_{i = 1}^{n} \sum_{j = 1}^{m} \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor [x | gcd(i, j)]$$

   把$x$提出来就可以忽略掉$gcd$了:

   $$F(x) = \sum_{i = 1}^{\lfloor \frac{n}{x} \rfloor} \sum_{j = 1}^{\lfloor \frac{m}{x} \rfloor} \lfloor \frac{n}{ix} \rfloor \lfloor \frac{m}{jx} \rfloor$$

   我们只要预处理出$sum[x] = \sum_{i = 1}^x \lfloor \frac{x}{i} \rfloor$即可$O(1)$计算$F(x)$

   

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int MAXN = 50010;
 5 
 6 int cnt, prime[MAXN], mu[MAXN], sum[MAXN];
 7 bool vis[MAXN];
 8 
 9 void init(int n)
10 {
11     mu[1] = 1;
12     for(int i = 2; i <= n; ++i)
13     {
14         if(!vis[i]) {prime[++cnt] = i; mu[i] = -1;}
15         for(int j = 1; j <= cnt && prime[j] * i <= n; ++j)
16         {
17             vis[i * prime[j]] = 1;
18             if(i % prime[j] == 0) break;
19             mu[i * prime[j]] = -mu[i];
20         }
21     }
22     for(int i = 1; i <= n; ++i) mu[i] += mu[i - 1];
23     for(int x = 1; x <= n; ++x)
24         for(int l = 1, r; l <= x; l = r + 1)
25         {
26             r = x / (x / l);
27             sum[x] += (r - l + 1) * (x / l);
28         }
29 }
30 
31 int T, n, m;
32 long long ans;
33 
34 int main()
35 {
36     init(MAXN - 10);
37     scanf("%d", &T);
38     while(T--)
39     {
40         scanf("%d %d", &n, &m);
41         if(n > m) swap(n, m); ans = 0;
42         for(int l = 1, r; l <= n; l = r + 1)
43         {
44             r = min(n / (n / l), m / (m / l));
45             ans += (long long)(mu[r] - mu[l - 1]) * sum[n / l] * sum[m / l];
46         }
47         printf("%lld\n", ans);
48     }
49     return 0;
50 }

 

   首先用二维前缀和搞一下,把式子拆成四部分:$(a,b,c,d) = (1,b,1,d) - (1,a-1,1,d) - (1,b,1,c-1) + (1,a-1,1,c-1)$

   然后有了第一个例题的经验,我们很容易反演到这一步:

      $$f(x) = \sum_{d=1}^{\min(\lfloor \frac{n}{x} \rfloor, \lfloor \frac{m}{x} \rfloor)} \mu(d) \lfloor \frac{n}{dx} \rfloor \lfloor \frac{m}{dx} \rfloor$$

   到了这一步就可以了,如果还像第一题那样继续推的话,你就会发现那样你需要预处理出$\mu$除以所有$k$的前缀和,空间和时间都无法接受

   计算的时候注意乘法爆$int$的问题,我因为这个$WA$了两遍

   

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int MAXN = 50010;
 5 typedef long long ll;
 6 
 7 int mu[MAXN], cnt, prime[MAXN];
 8 bool vis[MAXN];
 9 
10 void get_mu(int n)
11 {
12     mu[1] = 1;
13     for(int i = 2; i <= n; ++i)
14     {
15         if(!vis[i]) prime[++cnt] = i, mu[i] = -1;
16         for(int j = 1; j <= cnt && i * prime[j] <= n; ++j)
17         {
18             vis[i * prime[j]] = 1;
19             if(i % prime[j] == 0) break;
20             mu[i * prime[j]] = -mu[i];
21         }
22     }
23     for(int i = 1; i <= n; ++i) mu[i] += mu[i - 1];
24 }
25 
26 ll Calc(int n, int m, int k)
27 {
28     ll ans = 0; if(n > m) swap(n, m);
29     for(int l = 1, r; l <= n / k; l = r + 1)
30     {
31         r = min((n / k) / (n / k / l), (m / k) / (m / k / l));
32         ans += (long long)(mu[r] - mu[l - 1]) * ((long long)(n / k / l) * (m / k / l));
33     }
34     return ans;
35 }
36 
37 int main()
38 {
39     get_mu(MAXN - 10);
40     int n, a, b, c, d, k;
41     scanf("%d", &n);
42     while(n--)
43     {
44         scanf("%d %d %d %d %d", &a, &b, &c, &d, &k);
45         printf("%lld\n", Calc(b, d, k) - Calc(a - 1, d, k) - Calc(b, c - 1, k) + Calc(a - 1, c - 1, k));
46     }
47     return 0;
48 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!