P1447 [NOI2010]能量采集 莫比乌斯反演

P1447 [NOI2010]能量采集

题目链接

​ 莫比乌斯反演。

​ 简化题意: $\displaystyle \sum_{i = 1}^{n} \sum_{j = 1}^{m}2(gcd(i, j) - 1) + 1 $

甩链接

​ 化简完式子:\(-n*m +2* \displaystyle \sum_{T = 1}^{min(n, m)} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \phi(T)\)

\(O(n)\)可做。

​ 可以用整除分块优化一下。
一个数等于它因子的欧拉函数之和:\(n = \displaystyle \sum_{d \mid n} \phi(d)\)

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 1e5 + 5;
int n, m, cnt;
long long ans, phi[N];
int prime[N], is_prime[N];

void make_phi(int x) {
    phi[1] = 1;
    for(int i = 2;i <= x; i++) {
        if(!is_prime[i]) 
            phi[i] = i - 1, prime[++cnt] = i;
        for(int j = 1;j <= cnt && i * prime[j] <= x; j++) {
            is_prime[i * prime[j]] = 1;
            if(!(i % prime[j])) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
    for(int i = 1;i <= x; i++) phi[i] += phi[i - 1];
}

int main() {

    n = read(); m = read();
    int t = min(n, m);

    make_phi(t);
    // for(int i = 1;i <= min(n, m); i++) cout << i << " " << phi[i] << endl;

    int last;
    for(int  i = 1;i <= t; i = last + 1) {
        last = min(n / (n / i), m / (m / i));
        ans += 1ll * (n / i) * (m / i) * (phi[last] - phi[i - 1]);
    }

    printf("%lld", 1ll * ans * 2 - 1ll * n * m);

    return 0;
}
posted @ 2020-09-14 21:28  C锥  阅读(87)  评论(0编辑  收藏  举报