[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}}\rfloor \lfloor{\frac{m}{y}}\rfloor [gcd(x, y) == 1]$$<---改成枚举x和y,然后统计有多少对i,j中分别含有因数x,y
设$$f(d) = \sum_{x = 1}^{n}\sum_{y = 1}^{m} \lfloor{\frac{n}{x}}\rfloor \lfloor{\frac{m}{y}}\rfloor [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}}\rfloor \lfloor{\frac{m}{y}}\rfloor [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}}\rfloor$ ---> $dx$倍数的个数,即有多少i,j的搭配是可以满足$[d|gcd(x, y)]$的
$$g(d) = \sum_{x = 1}^{ \lfloor{\frac{n}{d}}\rfloor }\sum_{y = 1}^{ \lfloor{\frac{m}{d}}\rfloor } \lfloor{\frac{n}{dx}}\rfloor \lfloor{\frac{m}{dy}}\rfloor$$
于是现在就是要求$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}}\rfloor }\sum_{y = 1}^{ \lfloor{\frac{m}{d}}\rfloor } \lfloor{\frac{n}{dx}}\rfloor \lfloor{\frac{m}{dy}}\rfloor}$$
这个式子可以整数分块。
设$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}}\rfloor \lfloor{\frac{M}{y}}\rfloor}$$
$$= \sum_{d = 1}^{min(n, m)}{\mu(d) \cdot \sum_{x = 1}^{ N}{\lfloor{\frac{N}{x}}\rfloor} \sum_{y = 1}^{M} {\lfloor{\frac{M}{y}}\rfloor}}$$
而$N = \lfloor{\frac{n}{d}}\rfloor$, $M = \lfloor{\frac{m}{d}}\rfloor$有很多相同的区间段,所以可以整数分块求。
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\n", 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\n", 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 }