Loading

洛谷 P1891 疯狂 LCM

\(\text{lcm}\) 不好处理,转化为 \(\gcd\)

\[n \sum_{i = 1}^n \frac{i}{\gcd(i, n)} \]

\(\gcd\) 相关题目的套路——枚举因数:

\[n \sum_{d | n} \sum_{i = 1}^n \frac id [\gcd(\frac id, \frac nd) = 1] \]

整理:

\[n \sum_{d | n} \sum_{i = 1}^{\frac nd} i[\gcd(i, \frac nd) = 1] \]

因为枚举了 \(n\) 的每个因数 \(d\),所以 \(\dfrac nd \iff d\)

\[n \sum_{d | n} \sum_{i = 1}^{d} i[\gcd(i, d) = 1] \]

考虑 \(\sum\limits_{i = 1}^d i[\gcd(i, d) = 1]\) 的求解。

已知满足条件的 \(i\)\(\varphi(d)\) 个,受等差数列求和公式的启发,考虑将满足条件的 \(i\) 两两配对,使得每对的和是定值。

\(\gcd(i, d) = 1\),则一定有 \(\gcd(d - i, d) = 1\)

证明:

\(\gcd(d - i, d) = k\),令 \(d - i = xk, d = yk, i = (y - x)k\),易得 \(\gcd(x, y) = 1\),进一步可以得到 \(\gcd(y, y - x) = 1\)

两边同时乘上 \(k\),得 \(\gcd((y - x)k, yk) = k\),而 \(\gcd((y - x)k, yk) = \gcd(i, d) = 1\),所以 \(k = 1\),即 \(\gcd(d - i, d) = 1\)

\(\sum\limits_{i = 1}^d i[\gcd(i, d) = 1] = \dfrac{\varphi(d) \times d}2\)

特别地,\(d = 1\) 时,不存在满足上述条件的 \((i, d - i)\),此时 \(\sum\limits_{i = 1}^d i[\gcd(i, d) = 1] = 1\),直接累加进答案即可。

最后:

\(f(d) = \left\{ \begin{aligned} 1&, d = 1 \\ \frac{\varphi(d)}{2} \times d&, otherwise. \end{aligned} \right.\),则有:

\[ans = n \sum_{d | n} f(d) \]

时间复杂度 \(O(n + T \sqrt n)\)

代码:

#include <bits/stdc++.h>

#define MAXN 1000100

using namespace std;

typedef long long ll;

int T, n, phi[MAXN];
int cnt, prime[MAXN];
bool vis[MAXN];

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

template<typename _T>
void write(_T _x) {
    if (_x < 0) {
        putchar('-');
        _x = -_x;
    }
    if (_x > 9) write(_x / 10);
    putchar('0' + _x % 10);
}

void getphi(int n) {
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            phi[i] = i - 1;
            prime[++cnt] = i;
        }
        for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * phi[prime[j]];
        }
    }
}

ll f(int d) {
    if (d == 1) return 1;
    return (1ll * d * phi[d]) >> 1;
}

int main() {
    getphi(1e6);
    read(T);
    while (T--) {
        read(n);
        ll ans = 0;
        for (int i = 1; i * i <= n; i++) {
            if (n % i) continue;
            ans += f(i);
            if (i * i == n) break;
            ans += f(n / i);
        }
        write(ans * n), putchar('\n');
    }
    return 0;
}
posted @ 2022-12-01 16:17  Chy12321  阅读(18)  评论(0编辑  收藏  举报