NC23048 月月给华华出题

发布时间 2023-08-26 03:20:46作者: 空白菌

题目链接

题目

题目描述

因为月月是个信息学高手,所以她也给华华出了一题,让他求:
\(\sum_{i=1}^N\frac{i}{\gcd(i,N)}\)
但是因为这个式子实在太简单了,所以月月希望华华对N=1,2,...,n各回答一次。华华一脸懵逼,所以还是决定把这个问题丢给你。

输入描述

一个正整数n。

输出描述

输出n行,第i行表示N=i时的答案。

示例1

输入

6

输出

1
2
4
6
11
11

备注

\(1\le n\le 10^6\)
请注意输出的效率

题解

知识点:欧拉函数,因数集合,筛法。

简单推一下式子:

\[\begin{aligned} \sum_{i=1}^{n} \frac{i}{\gcd(i,n)} &= \sum_{d \mid n} \sum_{i=1}^{n} \frac{i}{d}[\gcd(i,n) = d]\\ &= \sum_{d \mid n} \sum_{i=1}^{\frac{n}{d}} i\left[\gcd\left(i,\frac{n}{d} \right) = 1 \right]\\ &= \sum_{d \mid n} \frac{\frac{n}{d}\varphi \left( \frac{n}{d} \right) + [\frac{n}{d} = 1]}{2}\\ &= \sum_{d \mid n} \frac{d\varphi(d) + [d = 1]}{2} \end{aligned} \]

于是对于一个 \(i = n\) 的答案,只要枚举其因数即可。但对每个 \(i\) 枚举因子的复杂度是 \(\sqrt i\) ,会超时,因此我们使用倍数法枚举 \(i \in [1,n]\) 的区间所有数的因子,可以直接累加答案,复杂度是 \(O(n \log n)\) 的。

需要先线性预处理欧拉函数。

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 1e6 + 7;
bool vis[N];
vector<int> prime;
int phi[N];
void get_euler(int n) {
    phi[1] = 1;
    for (int i = 2;i <= n;i++) {
        if (!vis[i]) {
            prime.push_back(i);
            phi[i] = i - 1;
        }
        for (auto j : prime) {
            if (i * j > n) break;
            vis[i * j] = 1;
            if (!(i % j)) {
                phi[i * j] = j * phi[i];
                break;
            }
            phi[i * j] = (j - 1) * phi[i];
        }
    }
}

ll ans[N];
void get_factor(int n) {
    for (int i = 1;i <= n;i++)
        for (int j = 1;i * j <= n;j++)
            ans[i * j] += (1LL * i * phi[i] + (i == 1)) / 2;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    get_euler(n);
    get_factor(n);
    for (int i = 1;i <= n;i++) cout << ans[i] << '\n';
    return 0;
}