bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛

题目传送门

  传送门I

  传送门II

题目大意

  定义函数$f(x)$为数$x$所含质因子的最大指数。求$\sum_{i = 1}^{a}\sum_{j = 1}^{b}f((i, j))$。

  继续用之前的套路统计每个$f$被计算的次数,然后将gcd化为莫比乌斯函数之和。

  $\begin{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_{e\mid i / d\wedge e | j / d} \mu(e)f(d) \right)\\ &= \sum_{d = 1}^{a} f(d)\sum_{e = 1}^{\left \lfloor a / d\right \rfloor}\left \lfloor\frac{a}{de}\right \rfloor\left \lfloor \frac{b}{de} \right \rfloor \mu(e) \end{align}$

  如果这样,似乎并没有什么好方法求值,所以试试把那几个向下取整的分数搞到外层来,这样就可以找机会分段处理。于是得到了下面这个式子:

$\sum_{T = 1}^{a}\left \lfloor \frac{a}{T} \right \rfloor\left \lfloor \frac{b}{T} \right \rfloor \sum_{d | T} \mu(T / d) f(d)$

  好了问题来了,如何把后面的值快速预处理出来,前面的是可以通过分段降低时间复杂度。

  为了方便描述,所以我们设$g(n) = \sum_{d | n}\mu(n / d)f(d)$。

  首先讲一下,当$S$是非空有限集合时,$\sum_{A\subseteq S}(-1)^{|A|} = 0$。

  首先对n进行质因数分解,得到$n = p_{1}^{\alpha_1}p_{2}^{\alpha_2}\cdots p_{k}^{\alpha_k}$。然后设$\alpha = \max\left \{\alpha_{1}, \alpha_{2}, \dots, \alpha_{k} \right \}$,$P = \left \{p_{1}, p_{2}, \dots, p_{k}\right \}, P' = \left \{ p: p\in P \wedge p^{\alpha} | n \right \}$,$\overline{P'}$是以$P$为全集,$P'$的补集。

  根据莫比乌斯函数的性质,对于任何一个数有一个质因子的指数大于1,则它的莫比乌斯函数值为0,即为了使式子对答案有贡献,就应当满足,当$d$分解后,$p_i$的指数要么是$\alpha_{i}$要么是$\alpha_{i} - 1$然后有

$g(n) = \sum_{A\subseteq P}\max\left \{ a_{i} - [p_{i} \in A](-1)^{|A|}\right \}$

  显然中间的$\max\left \{ a_{i} - [p_{i} \in A](-1)^{|A|}\right \} \in \left \{ \alpha, \alpha - 1\right \}$。所以我们可以把它拆成两部分求和。

  一部分是包含整个$P'$,另一部分是只包含$P'$的一部分。

$g(n) = \sum_{A\subseteq P} [P' \subseteq A](a - 1)(-1)^{|A|} + \sum_{A\subseteq P}[P' \cap A\subset P']a(-1)^{|A|}$

  然后前一部分事实上可以把$A$拆成$\overline{P'}$的子集和$P'$的并集,后一部分也可以把$A$拆成$P'$的真子集和$\overline{P'}$的子集。

$\begin{align} g(n) &= \sum_{X\subseteq \overline{P'}} (a - 1)(-1)^{|X| +|P'|} + \sum_{X\subseteq \overline{P'}} \sum_{Y\subset P'} (-1)^{|X| + |Y|}a \\ &= \sum_{X\subseteq \overline{P'}}\left [(-1)^{|X| + |P'| + 1} + \sum_{Y\subseteq P'} (-1)^{|X| + |Y|}a\right] \\ &= (-1)^{|P'| + 1}\sum_{X\subseteq \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 \nmid 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"\n", 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 }
posted @ 2017-07-27 22:42 阿波罗2003 阅读(...) 评论(...) 编辑 收藏