[洛谷 P1891] 疯狂 LCM (欧拉函数 莫比乌斯反演)

题意:

∑ i = 1 n lcm ( i , n ) \sum_{i=1} ^{n} \text{lcm}(i,n) i=1nlcm(i,n)

分析:

法一:欧拉函数

拆一下 lcm ( i , n ) = i ⋅ n gcd ⁡ ( i , n ) \text{lcm}(i,n) = \dfrac{i \cdot n}{\gcd{(i,n)}} lcm(i,n)=gcd(i,n)in 变为:

∑ i = 1 n i ⋅ n gcd ⁡ ( i , n ) \sum_{i=1} ^{n} \frac{i \cdot n}{\gcd{(i,n)}} i=1ngcd(i,n)in

枚举 gcd ⁡ ( i , n ) \gcd(i,n) gcd(i,n)

n ∑ d ∣ n ∑ i = 1 n i d [ gcd ⁡ ( i , n ) = d ] n \sum_{d \mid n} \sum_{i=1} ^{n} \frac{i }{d}[\gcd{(i,n)} = d ] ndni=1ndi[gcd(i,n)=d]

利用 gcd ⁡ \gcd gcd 的性质:

n ∑ d ∣ n ∑ i = 1 n i d [ gcd ⁡ ( i d , n d ) = 1 ] n \sum_{d \mid n} \sum_{i=1} ^{n} \frac{i }{d}[\gcd{(\frac{i}{d},\frac{n}{d})} = 1 ] ndni=1ndi[gcd(di,dn)=1]

d d d 拿到上界

n ∑ d ∣ n ∑ i = 1 ⌊ n d ⌋ i [ gcd ⁡ ( i , n d ) = 1 ] n \sum_{d \mid n} \sum_{i=1} ^{ \lfloor \frac{n}{d} \rfloor } i[\gcd{(i,\frac{n}{d})} = 1 ] ndni=1dni[gcd(i,dn)=1]

⌊ n d ⌋ \lfloor \dfrac{n}{d} \rfloor dn 等价于 d d d

n ∑ d ∣ n ∑ i = 1 d i [ gcd ⁡ ( i , d ) = 1 ] n \sum_{d \mid n} \sum_{i=1} ^{ d } i[\gcd{(i,d)} = 1 ] ndni=1di[gcd(i,d)=1]

由于 gcd ⁡ ( i , d ) = gcd ⁡ ( d − i , d ) \gcd(i, d) = \gcd(d - i,d) gcd(i,d)=gcd(di,d) ,所以因子必成对出现(除了1),那么总共出现了 φ ( d ) 2 \dfrac{\varphi(d)}{2} 2φ(d) 次, d − i + i = d d - i + i =d di+i=d,所以就是

n ∑ d ∣ n φ ( d ) 2 d n \sum_{d \mid n} \frac{\varphi(d)}{2} d ndn2φ(d)d

这样时间复杂度是 O ( N + T n ) O(N+T\sqrt{n}) O(N+Tn ),但是可以用狄利克雷卷积优化,可以做到 O ( N log ⁡ N + T ) O(N \log{N} + T) O(NlogN+T)

F ( x ) = x ⋅ φ ( x ) 2 F(x) = \dfrac{x \cdot \varphi(x)}{2} F(x)=2xφ(x)

则答案为 n ⋅ F ∗ 1 n \cdot F * \textbf{1} nF1,注意处理 d = 1 d=1 d=1 的情况。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int T, n, primes[N], euler[N], cnt, res, f[N];
bool st[N];
void get_eulers(int n) {
    euler[1] = 1;
    for (int i = 2; i <= n; i ++) {
        if (!st[i]) {
            primes[cnt ++] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; primes[j] <= n / i; j ++) {
            int t = primes[j] * i;
            st[t] = 1;
            if (i % primes[j] == 0) {
                euler[t] = primes[j] * euler[i];
                break;
            }
            euler[t] = (primes[j] - 1) * euler[i];
        }
    }
    for (int i = 1; i <= n; i ++) {
        for (int j = i; j <= n; j += i) {
            f[j] += (euler[i] * i + 1) / 2;
        }
    }
}
signed main() {
    get_eulers(N - 1);
    cin >> T;
    while (T --) {
        cin >> n;
        cout << n * f[n] << endl;
    }
}

法二:莫比乌斯反演

还是法一的式子,推到这一步

n ∑ d ∣ n ∑ i = 1 d i [ gcd ⁡ ( i , d ) = 1 ] n \sum_{d \mid n} \sum_{i=1} ^{ d } i[\gcd{(i,d)} = 1 ] ndni=1di[gcd(i,d)=1]

用单位函数替换

n ∑ d ∣ n ∑ i = 1 d i ⋅ ε ( gcd ⁡ ( i , d ) ) n \sum_{d \mid n} \sum_{i=1} ^{ d } i \cdot \varepsilon (\gcd{(i,d)}) ndni=1diε(gcd(i,d))

莫比乌斯反演

n ∑ d ∣ n ∑ i = 1 d i ∑ k ∣ gcd ⁡ ( i , d ) μ ( k ) n \sum_{d \mid n} \sum_{i=1} ^{ d } i \sum_{k \mid \gcd(i,d) } \mu(k) ndni=1dikgcd(i,d)μ(k)

交换枚举次序

n ∑ d ∣ n ∑ k ∣ d k μ ( k ) ∑ i = 1 ⌊ d k ⌋ i n \sum_{d \mid n} \sum _{k \mid d} k\mu(k) \sum_{i=1} ^{ \lfloor \frac{d}{k} \rfloor } i ndnkdkμ(k)i=1kdi

对后半部分求和

n 2 ∑ d ∣ n ∑ k ∣ d k μ ( k ) ( ⌊ d k ⌋ 2 + ⌊ d k ⌋ ) \frac{n}{2} \sum_{d \mid n} \sum _{k \mid d} k\mu(k) (\lfloor \frac{d}{k} \rfloor ^ 2 + \lfloor \frac{d}{k} \rfloor) 2ndnkdkμ(k)(kd2+kd)

可以用狄利克雷卷积优化到 O ( N log ⁡ N + T ) O(N\log N +T) O(NlogN+T)

f ( x ) = x ⋅ μ ( x ) f(x)=x \cdot \mu(x) f(x)=xμ(x) g ( x ) = x 2 + x g(x)=x^2+x g(x)=x2+x F ( x ) = f ∗ g F(x) = f * g F(x)=fg

那么答案就为:

n 2 ⋅ F ∗ 1 \frac{n}{2} \cdot F * \textbf {1} 2nF1

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int T, n, mobius[N], primes[N], cnt, F[N], ans[N];
bool st[N];
void get_mobius(int n) {
    mobius[1] = 1;
    for (int i = 2; i <= n; i ++) {
        if (!st[i]) {
            primes[cnt ++] = i;
            mobius[i] = -1;
        }
        for (int j = 0; primes[j] * i <= n; j ++) {
            int t = primes[j] * i;
            st[t] = 1;
            if (i % primes[j] == 0) {
                mobius[t] = 0;
                break;
            }
            mobius[t] = -mobius[i];
        }
    }
    for (int i = 1; i <= n; i ++) {
        for (int j = i; j <= n; j += i) {
            F[j] += i * mobius[i] * ((j / i) * (j / i) + (j / i));
        }
    }
    for (int i = 1; i <= n; i ++) {
        for (int j = i; j <= n; j += i) {
            ans[j] += F[i];
        } 
    }
}
signed main() {
    get_mobius(N - 1);
    cin >> T;
    while (T --) {
        cin >> n;
        cout << n * ans[n] / 2 << endl;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值