「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;
}
posted @ 2020-06-15 22:00  Sangber  阅读(223)  评论(1编辑  收藏  举报