「NOI2016」循环之美
这个题面一眼看过去没什么想法,尝试转化题意。
假设我们现在有合法的一组 \(x, y\),显然我们先要有 \(x \perp y\)。
设 \(\frac{x}{y}\) 在 \(k\) 进制下是一个循环节长度为 \(l\) 的循环小数。
那么我们显然可以得到 \([\frac{xk^l}{y}] = [\frac{x}{y}]\),也就是两者的小数部分相等。
那么我们可以进一步推导:
\[\begin{split}
[\frac{xk^l}{y}] = [\frac{x}{y}]
& \Rightarrow \frac{xk^l}{y} - \lfloor \frac{xk^l}{y} \rfloor = \frac{x}{y} - \lfloor \frac{x}{y} \rfloor \\
& \Rightarrow xk^l - \lfloor \frac{xk^l}{y} \rfloor \times y = x - \lfloor \frac{x}{y} \rfloor \times y \\
& \Rightarrow xk^l \equiv x (\bmod\ y) \\
& \Rightarrow k^l \equiv 1 (\bmod\ y)\quad\quad(\gcd(x, y) = 1) \\
& \Rightarrow k \perp y
\end{split}
\]
推导到这里我们就不难发现其实我们是要求一个这样的东西:
\[f(n, m, k) = \sum_{i = 1} ^ n \sum_{j = 1} ^ m [i \perp j] [j \perp k]
\]
考虑化简后面那一坨:
\[\begin{split}
f(n, m, k) & = \sum_{i = 1} ^ n \sum_{j = 1} ^ m [i \perp j] [j \perp k] \\
& = \sum_{i = 1} ^ n \sum_{j = 1} ^ m [i \perp j] \sum_{d | j, d | k} \mu(d) \\
& = \sum_{d = 1} ^ {\min(n, m)} \mu(d) \sum_{d | j} \sum_{i = 1} ^ n [i \perp j] \\
& = \sum_{d = 1} ^ {\min(n, m)} \mu(d) \sum_{j = 1} ^ {\lfloor \frac{m}{d} \rfloor} \sum_{i = 1} ^ n [j \perp i] [i \perp d] \\
& = \sum_{d = 1} ^ {\min(n, m)} \mu(d) f(\lfloor \frac{m}{d} \rfloor, n, d)
\end{split}
\]
边界:
\(f(n, 0, k) = f(0, m, k) = 0, f(n, m, 1) = \sum_{i = 1} ^ n \sum_{j = 1} ^ m [i \perp j] = \sum_{d = 1} ^ {\min(n, m)} \mu(d) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor\)
杜教筛 \(\mu\),然后递归求就是了。
参考代码:
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
typedef long long LL;
const int _ = 1e7 + 5;
int vis[_], num, pri[_]; LL mu[_], s[_];
vector < int > fac;
map < int, LL > ans;
void seive() {
mu[1] = 1;
for (int i = 2; i < _; ++i) {
if (!vis[i]) mu[i] = -1, pri[++num] = i;
for (int j = 1; j <= num && i * pri[j] < _; ++j) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) break ;
mu[i * pri[j]] = -mu[i];
}
}
for (int i = 1; i < _; ++i) s[i] = s[i - 1] + mu[i];
}
LL Sum(int n) {
if (n < _) return s[n]; else if (ans[n]) return ans[n];
LL res = 1;
for (int l = 2, r; l <= n; l = r + 1)
r = n / (n / l), res -= 1ll * (r - l + 1) * Sum(n / l);
return ans[n] = res;
}
LL solve(int n, int m, int k) {
if (!n || !m) return 0;
LL res = 0;
if (k == 1) {
for (int l = 1, r; l <= min(n, m); l = r + 1)
r = min(n / (n / l), m / (m / l)), res += 1ll * (Sum(r) - Sum(l - 1)) * (n / l) * (m / l);
return res;
}
for (int i = 0; i < fac.size(); ++i)
if (k % fac[i] == 0 && mu[fac[i]])
res += mu[fac[i]] * solve(m / fac[i], n, fac[i]);
return res;
}
int n, m, k;
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
seive();
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i * i <= k; ++i)
if (k % i == 0) {
fac.push_back(i);
if (i * i != k) fac.push_back(k / i);
}
printf("%lld\n", solve(n, m, k));
return 0;
}