继续用之前的套路统计每个$f$被计算的次数,然后将gcd化为莫比乌斯函数之和。
$egin{align} sum_{i = 1}^{a}sum_{j = 1}^{b} f((i, j)) &= sum_{d = 1}^{a} sum_{i = 1}^{a}sum_{j = 1}^{b}[(i, j) = d]f(d) \ &= sum_{d = 1}^{a}sum_{i = 1}^{a}sum_{j = 1}^{b} left( sum_{emid i / dwedge e | j / d} mu(e)f(d) ight)\ &= sum_{d = 1}^{a} f(d)sum_{e = 1}^{left lfloor a / d ight floor}left lfloorfrac{a}{de} ight floorleft lfloor frac{b}{de} ight floor mu(e) end{align}$
如果这样,似乎并没有什么好方法求值,所以试试把那几个向下取整的分数搞到外层来,这样就可以找机会分段处理。于是得到了下面这个式子:
$sum_{T = 1}^{a}left lfloor frac{a}{T} ight floorleft lfloor frac{b}{T} ight floor sum_{d | T} mu(T / d) f(d)$
好了问题来了,如何把后面的值快速预处理出来,前面的是可以通过分段降低时间复杂度。
为了方便描述,所以我们设$g(n) = sum_{d | n}mu(n / d)f(d)$。
首先讲一下,当$S$是非空有限集合时,$sum_{Asubseteq S}(-1)^{|A|} = 0$。
首先对n进行质因数分解,得到$n = p_{1}^{alpha_1}p_{2}^{alpha_2}cdots p_{k}^{alpha_k}$。然后设$alpha = maxleft {alpha_{1}, alpha_{2}, dots, alpha_{k} ight }$,$P = left {p_{1}, p_{2}, dots, p_{k} ight }, P' = left { p: pin P wedge p^{alpha} | n ight }$,$overline{P'}$是以$P$为全集,$P'$的补集。
根据莫比乌斯函数的性质,对于任何一个数有一个质因子的指数大于1,则它的莫比乌斯函数值为0,即为了使式子对答案有贡献,就应当满足,当$d$分解后,$p_i$的指数要么是$alpha_{i}$要么是$alpha_{i} - 1$然后有
$g(n) = sum_{Asubseteq P}maxleft { a_{i} - [p_{i} in A](-1)^{|A|} ight }$
显然中间的$maxleft { a_{i} - [p_{i} in A](-1)^{|A|} ight } in left { alpha, alpha - 1 ight }$。所以我们可以把它拆成两部分求和。
一部分是包含整个$P'$,另一部分是只包含$P'$的一部分。
$g(n) = sum_{Asubseteq P} [P' subseteq A](a - 1)(-1)^{|A|} + sum_{Asubseteq P}[P' cap Asubset P']a(-1)^{|A|}$
然后前一部分事实上可以把$A$拆成$overline{P'}$的子集和$P'$的并集,后一部分也可以把$A$拆成$P'$的真子集和$overline{P'}$的子集。
$egin{align} g(n) &= sum_{Xsubseteq overline{P'}} (a - 1)(-1)^{|X| +|P'|} + sum_{Xsubseteq overline{P'}} sum_{Ysubset P'} (-1)^{|X| + |Y|}a \ &= sum_{Xsubseteq overline{P'}}left [(-1)^{|X| + |P'| + 1} + sum_{Ysubseteq P'} (-1)^{|X| + |Y|}a ight] \ &= (-1)^{|P'| + 1}sum_{Xsubseteq overline{P'}}(-1)^{|X|} end{align}$
接下来考虑$overline{P'}$。
1) 如果它不为空的话,那么意味着求和符号下的值为$0$。换言之,就是$n$中的所有幂指数不相等,那么就有$g(n) = 0$。
2) 如果它为空的话,意味着求和符号的值为$1$,$P'$的大小就是$k$,那么就有$g(n) = (-1)^{k + 1}$。
然后有什么用呢?
考虑预处理,由于$10^{7}$的凶残数据范围所以不能够$O(n log n)$预处理。就考虑线性筛。
这里要用到线性筛一个很重要的性质,每个合数保证被其最小的质因子筛掉。所以,我们只需要记录每个数的最小质因子的次数$pos_i$。
- 当$prime_j mid i$时,我们就可以轻松处理掉(判断最小质因子的次数是否为$1$,可以得到$g(i)$是否为0)。
- 当$prime_j | i$的时候,考虑$i$把$prime_j$除尽后的数$x$,此时可以判断一下$pos_x$和$pos_i + 1$是否相等,更新$g$值就好了。
因此,我们还需要记录每个数把最小质因子除去后的数。这个很好处理。
Code
1 /** 2 * bzoj 3 * Problem#3309 4 * Accepted 5 * Time:11804ms 6 * Memory:170040k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 typedef bool boolean; 16 17 #define LL long long 18 19 const int limit = 1e7; 20 21 int num = 0; 22 int prime[700100]; 23 boolean vis[limit + 1]; 24 int last[limit + 1]; 25 int posm[limit + 1]; 26 LL g[limit + 1]; 27 inline void Euler() { 28 memset(vis, false, sizeof(vis)); 29 g[0] = g[1] = -1; 30 for(int i = 2; i <= limit; i++) { 31 if(!vis[i]) prime[num++] = i, last[i] = 1, posm[i] = 1, g[i] = 1; 32 for(int j = 0; j < num && i * 1LL * prime[j] <= limit; j++) { 33 int c = i * prime[j]; 34 vis[c] = true; 35 if(i % prime[j]) { 36 last[c] = i, posm[c] = 1; 37 g[c] = (posm[i] == 1) ? (-g[i]) : (0); 38 } else { 39 last[c] = last[i]; 40 posm[c] = posm[i] + 1; 41 g[c] = (posm[last[c]] == posm[c] || last[c] == 1) ? (-g[last[c]]) : (0); 42 break; 43 } 44 } 45 } 46 for(int i = 2; i <= limit; i++) 47 g[i] += g[i - 1]; 48 } 49 50 int a, b; 51 inline void init() { 52 scanf("%d%d", &a, &b); 53 } 54 55 inline void solve() { 56 if(a > b) swap(a, b); 57 long long res = 0; 58 for(int i = 1, j; i <= a; i = j + 1) { 59 j = min(a / (a / i), b / (b / i)); 60 res += (a / i) * 1LL * (b / i) * (g[j] - g[i - 1]); 61 } 62 printf(Auto" ", res); 63 } 64 65 int T; 66 int main() { 67 Euler(); 68 scanf("%d", &T); 69 while(T--) { 70 init(); 71 solve(); 72 } 73 return 0; 74 }