[SDOI2015]约数个数和 莫比乌斯反演

~~~题面~~~

题解:

为什么SDOI这么喜欢莫比乌斯反演,,,

首先有一个结论$$d(ij) = sum_{x|i}sum_{y|j}[gcd(x, y) == 1]$$
为什么呢?
首先,可以看做从两个数中分别取一些不重叠的质数的$k_{i}$次方,组成新数的方案数。
那如果有需要重叠的部分怎么办?
可以看做全都在第一个or第二个数中取。
但是一个数的次数不够怎么办呢?
相当于以1为媒介,可以简介统计到这些情况
比如$2^{3} cdot 2^{2}  = 2^{5}$可以看成$1, 2^{3}$ + $1, 2^{2}$,这样就可以做到分别枚举了指数从1到5的情况了;
$$ans = sum_{i = 1}^{n}sum_{j = 1}^{m}sum_{x | i}sum_{y | j}[gcd(x, y) == 1]$$
$$ans = sum_{x = 1}^{n}sum_{y = 1}^{m} lfloor{frac{n}{x}} floor lfloor{frac{m}{y}} floor [gcd(x, y) == 1]$$<---改成枚举x和y,然后统计有多少对i,j中分别含有因数x,y
设$$f(d) = sum_{x = 1}^{n}sum_{y = 1}^{m} lfloor{frac{n}{x}} floor lfloor{frac{m}{y}} floor [gcd(x, y) == d]$$, $$g(x) = sum_{x | d}^{min(n, m)}{f(d)}$$
那么$ans = f(1)$
反演一下得到:$$f(n)  = sum_{n | d}mu(frac{d}{n}) cdot g(d)$$
接下来就是怎么求$g(d)$了。
$$g(d) = sum_{x = 1}^{n}sum_{y = 1}^{m} lfloor{frac{n}{x}} floor lfloor{frac{m}{y}} floor [d | gcd(x, y)]$$
$d | gcd(x, y)$ ---> $d | x + d | y$ ,因为$x | n + y | m$ ---> $d | n + d | m$
所以枚举$d_{i}$就符合条件了,$lfloor{frac{n}{dx}} floor$ ---> $dx$倍数的个数,即有多少i,j的搭配是可以满足$[d|gcd(x, y)]$的
$$g(d) = sum_{x = 1}^{ lfloor{frac{n}{d}} floor }sum_{y = 1}^{ lfloor{frac{m}{d}} floor } lfloor{frac{n}{dx}} floor lfloor{frac{m}{dy}} floor$$
于是现在就是要求$f(1)$
$$f(1) = sum_{d = 1}^{min(n, m)}{mu(frac{d}{1})g(d)}$$
$$= sum_{d = 1}^{min(n, m)}{mu(d)g(d)}$$
$$= sum_{d = 1}^{min(n, m)}{mu(d) cdot sum_{x = 1}^{ lfloor{frac{n}{d}} floor }sum_{y = 1}^{ lfloor{frac{m}{d}} floor } lfloor{frac{n}{dx}} floor lfloor{frac{m}{dy}} floor}$$
这个式子可以整数分块。
设$N = frac{n}{d}$, $M = frac{m}{d}$,那么
$$= sum_{d = 1}^{min(n, m)}{mu(d) cdot sum_{x = 1}^{ N }sum_{y = 1}^{ M } lfloor{frac{N}{x}} floor lfloor{frac{M}{y}} floor}$$
$$= sum_{d = 1}^{min(n, m)}{mu(d) cdot sum_{x = 1}^{ N}{lfloor{frac{N}{x}} floor} sum_{y = 1}^{M}  {lfloor{frac{M}{y}} floor}}$$
而$N = lfloor{frac{n}{d}} floor$, $M = lfloor{frac{m}{d}} floor$有很多相同的区间段,所以可以整数分块求。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 50100
 5 #define ac 50000
 6 #define LL long long
 7 int n, m, tot, t;
 8 LL ans;
 9 int prime[AC];
10 LL mu[AC], s[AC];
11 bool z[AC];
12 
13 inline int read()
14 {
15     int x = 0;char c = getchar();
16     while(c > '9' || c < '0') c = getchar();//error!!!又一次打错了读入优化。。。是||啊
17     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
18     return x;
19 }
20 
21 void pre()
22 {
23     int x;
24     mu[1] = 1;
25     for(R i = 2; i <= ac; i++)
26     {
27         if(!z[i]) prime[++tot] = i, mu[i] = -1;
28         for(R j = 1; j <= tot; j++)
29         {
30             x = prime[j];
31             if(x * i > ac) break;
32             z[x * i] = true;
33             if(!(i % x)) break;
34             mu[i * x] = -mu[i]; 
35         }
36     }
37     for(R i = 1; i <= ac; i++) mu[i] += mu[i-1];    
38     int pos;
39     for(R i = 1; i <= ac; i++) 
40     {
41         for(R j = 1; j <= i; j = pos + 1)
42         {
43             pos = i / (i / j);
44             s[i] += (LL)(i / j) * (LL)(pos - j + 1);
45         }
46     }
47 //    for(R i = 1; i <= 30; i++) printf("%lld
", s[i]);
48 }
49 
50 void work()
51 {
52     t = read();
53     while(t--)
54     {
55         n = read(), m = read(), ans = 0;
56         //scanf("%d%d", &n, &m);ans = 0;
57         int pos, b = min(n, m);
58         for(R i = 1; i <= b; i = pos + 1)
59         {
60             pos = min(n / (n / i), m / (m / i));
61             ans += (mu[pos] - mu[i - 1]) * s[n / i] * s[m / i];
62         }
63         printf("%lld
", ans);
64     }
65 }
66 
67 int main()
68 {
69 //    freopen("in.in", "r", stdin);
70     pre();
71     work();
72 //    fclose(stdin);
73     return 0;
74 }
原文地址:https://www.cnblogs.com/ww3113306/p/9488765.html