这个题面一眼看过去没什么想法,尝试转化题意。
假设我们现在有合法的一组 (x, y),显然我们先要有 (x perp y)。
设 (frac{x}{y}) 在 (k) 进制下是一个循环节长度为 (l) 的循环小数。
那么我们显然可以得到 ([frac{xk^l}{y}] = [frac{x}{y}]),也就是两者的小数部分相等。
那么我们可以进一步推导:
[egin{split}
[frac{xk^l}{y}] = [frac{x}{y}]
& Rightarrow frac{xk^l}{y} - lfloor frac{xk^l}{y}
floor = frac{x}{y} - lfloor frac{x}{y}
floor \
& Rightarrow xk^l - lfloor frac{xk^l}{y}
floor imes y = x - lfloor frac{x}{y}
floor imes y \
& Rightarrow xk^l equiv x (mod y) \
& Rightarrow k^l equiv 1 (mod y)quadquad(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]
]
考虑化简后面那一坨:
[egin{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}
floor} sum_{i = 1} ^ n [j perp i] [i perp d] \
& = sum_{d = 1} ^ {min(n, m)} mu(d) f(lfloor frac{m}{d}
floor, 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} floor lfloor frac{m}{d} floor)
杜教筛 (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
", solve(n, m, k));
return 0;
}