链接 UOJ 221 BZOJ 4652 LOJ 2085 
 
题解 令 $x \perp y$ 表示 $x, y$ 互质,若 $\frac x y, x \perp y$ 是纯循环小数,则一定存在一个正整数 $l$ 使得
$$\begin{aligned} x \cdot k ^ l &\equiv x \ (\text{mod } y)\\ k ^ l &\equiv 1 \ (\text{mod } y) \end{aligned}$$
根据欧拉定理,若 $k \perp y$,则 $k ^ {\varphi(y)} \equiv 1 \ (\text{mod } y)$,由于逆元是唯一的,所以存在 $l$ 的充要条件是 $k \perp y$,故问题转化为求
$$\begin{aligned}&\sum_{x=1}^{n} \sum_{y=1}^{m}[x\perp y][y \perp k]\\=&\sum_{y=1}^{m}[y\perp k]\sum_{x=1}^{n}[x\perp y]\\
=& \sum_{y = 1} ^ m[y \perp k]\sum_{x = 1} ^ n[\gcd(x, y) = 1]\end{aligned}$$
后半部分是一个经典的莫比乌斯反演,我们可以得到
$$\begin{aligned}\sum_{y = 1} ^ m[y \perp k]\sum_{d | y} ^ n\mu(d)\lfloor \frac n d \rfloor \end{aligned}$$
交换一下枚举顺序,得
$$\begin{aligned}&\sum_{d=1}^{n}\mu(d)\lfloor \frac{n}{d} \rfloor \sum_{y=1}^{m}[d\ |\ y][y\perp k]\\=&\sum_{d=1}^{n}\mu(d)\lfloor \frac{n}{d}\rfloor \sum_{i=1}^{\lfloor \frac{m}{d} \rfloor}[id\perp k]\\=& \sum_{d=1}^{n}[d\perp k]\mu(d)\lfloor \frac{n}{d}\rfloor \sum_{i=1}^{\lfloor \frac{m}{d} \rfloor}[i\perp k]\end{aligned}$$
令
$$f(n, k) = \sum_{i = 1} ^ n[i \perp k]$$
由于
$$[a \perp b] = [a \text{ mod }b \perp b]$$
则
$$f(n, k) = \lfloor \frac n k \rfloor f(k, k) + f(n \text{ mod } k, k)$$
此时我们已经解决了原式的后半部分,现在考虑如何快速求出前半部分,令
$$g(n, k) = \sum_{i = 1} ^ n [i \perp k]\mu(i)$$
考虑 $k$ 的一个质因数 $p$,那么 $k$ 可以表示为 $p ^ cq$,由于 $[1, n]$ 只有与 $k$ 互质的才是有效值,那么若 $x \perp k$,则 $x \perp p$ 且 $x \perp q$,我们可以考虑从 $x \perp q$ 的取值中减去 $x$ 不与 $p$ 互质的部分,就可以得到 $x \perp k$ 的部分,故
$$\begin{aligned}g(n,k)&=\sum_{i=1}^n[i\perp q]\mu(i)-\sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[py\perp q]\mu(py) \\&=g(n,q)- \sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[y\perp q]\mu(py)\end{aligned}$$
当 $p \perp y$ 时,$\mu(py) = \mu(p)\mu(y)$,否则 $\mu(py) = 0$,则
$$\begin{aligned}g(n,k)&=g(n,q)- \sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[y\perp p][y\perp q]\mu(p)\mu(y)\\&=g(n,q)-\mu(p)\sum_{y=1}^{\lfloor\frac{n}{p}\rfloor}[y\perp k]\mu(y)\\&=g(n,q)+g(\lfloor\frac{n}{p}\rfloor,k) \end{aligned}$$
此时我们就可以递归求解了,考虑边界,当 $n = 0$ 或 $k = 1, n = 0$ 时,直接返回 $0$ 就可以了,$k = 1$ 时,就是莫比乌斯函数的前缀和,这时我们用杜教筛求就好了。 时间复杂度 $O(\omega(k)\sqrt{n} + n ^ {\frac 2 3})$
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 #include  <bits/stdc++.h>  #include  <tr1/unordered_map>  namespace  Task {#define  long long long const  int  MAXN = 300010 ;const  int  MAXK = 2010 ;int  prime[MAXN], cnt, n, m, k;long  mu[MAXN];bool  vis[MAXN];inline  void  fastLinearSieveMethod (const  int  n)   {    mu[1 ] = 1 ;     for  (register  int  i = 2 ; i <= n; i++) {         if  (!vis[i]) prime[cnt++] = i, mu[i] = -1 ;         for  (register  int  j = 0 , tmp; j < cnt && (tmp = i * prime[j]) <= n;              j++) {             vis[tmp] = true ;             if  (i % prime[j] == 0 ) {                 mu[tmp] = 0 ;                 break ;             } else  {                 mu[tmp] = -mu[i];             }         }     }     for  (register  int  i = 1 ; i <= n; i++) mu[i] += mu[i - 1 ]; } typedef  std ::tr1::unordered_map <int , long > HashMap;HashMap map [MAXK]; HashMap::iterator it; const  int  sieveBlockSize = 300000 ;HashMap buc; inline  long  sieveMuMain (int  x)   {    if  (x <= sieveBlockSize) return  mu[x];     register  long  &cur = buc[x];     if  (cur) return  cur;     register  long  ret = 1 ;     for  (register  int  i = 2 , pos; i <= x; i = pos + 1 )         pos = x / (x / i), ret -= (pos - i + 1 ) * sieveMuMain(x / i);     return  cur = ret; } int  f[MAXK], fac[MAXK], facCnt;inline  void  initFunctionF ()   {    for  (register  int  i = 1 ; i <= k; i++)         f[i] = f[i - 1 ] + (std ::__gcd(i, k) == 1 ); } inline  long  getF (int  x)   { return  (long )(x / k) * f[k] + f[x % k]; }inline  long  getG (int  x, int  y)   {    if  (x <= 1 ) return  x;     if  (y == 0 ) return  sieveMuMain(x);     register  long  &cur = map [y][x];     return  cur ? cur : cur = getG(x, y - 1 ) + getG(x / fac[y], y); } inline  void  initFactor ()   {    register  int  now = k;     for  (register  int  i = 2 ; i <= k; i++) {         if  (now % i != 0 ) continue ;         fac[++facCnt] = i;         while  (now % i == 0 ) now /= i;         if  (now == 1 ) break ;     } } inline  void  solve ()   {    std ::ios::sync_with_stdio(false );     std ::cin .tie(NULL ), std ::cout .tie(NULL );     std ::cin  >> n >> m >> k;     fastLinearSieveMethod(sieveBlockSize);     initFunctionF(), initFactor();     register  int  min = std ::min(n, m);     register  long  ans = 0 ;     for  (register  int  i = 1 , pos; i <= min; i = pos + 1 ) {         pos = std ::min(n / (n / i), m / (m / i));         ans += (long )getF(m / i) * (n / i) *                (getG(pos, facCnt) - getG(i - 1 , facCnt));     }     std ::cout  << ans; } } int  main ()   {    Task::solve();     return  0 ; }