SPOJ5971 LCMSUM(反演+欧拉函数性质+积性函数(线性)筛)

传送门


题目大意

∑ i = 1 n l c m ( i , n ) \sum_{i=1}^nlcm(i,n) i=1nlcm(i,n)

反演

根据常见套路,首先我们化成含 g c d gcd gcd的式子: ∑ i = 1 n i ∗ n g c d ( i , n ) \sum_{i=1}^n\frac{i*n}{gcd(i,n)} i=1ngcd(i,n)in

然后枚举 g c d ( i , n ) gcd(i,n) gcd(i,n),得到 n ∑ i = 1 n ∑ d = 1 , d ∣ n n i d [ g c d ( i , n ) = d ] n\sum_{i=1}^n\sum_{d=1,d|n}^n\frac{i}{d}[gcd(i,n)=d] ni=1nd=1,dnndi[gcd(i,n)=d]

再根据 [ g c d ( i d , n d ) = 1 ] ⇔ [ g c d ( i , n ) = d ] [gcd(\frac{i}{d},\frac{n}{d})=1] \Leftrightarrow [gcd(i,n)=d] [gcd(di,dn)=1][gcd(i,n)=d]

i = x ∗ d i=x*d i=xd,枚举 x ∈ [ 1 , n ] x\in [1,n] x[1,n],代入 i i i后刚好消去了 1 i \frac{1}{i} i1,得到 n ∑ d ∣ n ∑ x = 1 ⌊ n d ⌋ x ∗ [ g c d ( x , ⌊ n d ⌋ ) = 1 ] n\sum_{d|n} \sum_{x=1}^{\lfloor \frac{n}{d}\rfloor} x*[gcd(x,\lfloor \frac{n}{d}\rfloor)=1] ndnx=1dnx[gcd(x,dn)=1]

欧拉函数性质

∑ x = 1 ⌊ n d ⌋ x ∗ [ g c d ( x , ⌊ n d ⌋ ) = 1 ] \sum_{x=1}^{\lfloor \frac{n}{d}\rfloor} x*[gcd(x,\lfloor \frac{n}{d}\rfloor)=1] x=1dnx[gcd(x,dn)=1]即小于 ⌊ n d ⌋ \lfloor \frac{n}{d}\rfloor dn且与 ⌊ n d ⌋ \lfloor \frac{n}{d}\rfloor dn互质的数之和

∑ i = 1 n i ∗ [ g c d ( i , n ) = 1 ] = { 1 n = 1 φ ( n ) n 2 n ≥ 2 \sum_{i=1}^ni*[gcd(i,n)=1]= \left\{\begin{array}{rcl} 1 && n=1\\ \frac{\varphi(n)n}{2} && n \geq 2\end{array}\right. i=1ni[gcd(i,n)=1]={12φ(n)nn=1n2

证明:

  • n = 1 n=1 n=1时显然为 1 1 1
  • n ≥ 2 n\geq 2 n2时, φ ( n ) \varphi(n) φ(n)总为偶数,因为和 n n n互质的数总是成对出现,假设 n n n i i i互质,那么 n − i n-i ni i i i也互质,他们的和为 n n n,并且有 φ ( n ) \varphi(n) φ(n)对,那么答案就是 φ ( n ) n 2 \frac{\varphi(n)n}{2} 2φ(n)n

那么最后的答案显然是 n ( n + ∑ d ≥ 2 , d ∣ n φ ( d ) d 2 ) n(n+\sum_{d \geq 2,d|n} \frac{\varphi(d)d}{2}) n(n+d2,dn2φ(d)d),这里的 d d d取遍 n n n的所有因数时实际上和 ⌊ n d ⌋ \lfloor \frac{n}{d}\rfloor dn等价

考虑完上述性质,我们就可以得到类似埃氏筛预处理的代码了:

时间复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)


//
// Created by Happig on 2020/9/28
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e6 + 10;

vector<int> prime;
bitset<maxn> vis;
ll phi[maxn], sum[maxn];

void init() {
    phi[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!vis[i]) {
            prime.push_back(i);
            phi[i] = i - 1;
        }
        for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j]) {
                phi[i * prime[j]] = phi[i] * phi[prime[j]];
            } else {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
        }
    }
    sum[1] = 1;
    for (int i = 2; i < maxn; i++) {
        sum[i]++;
        for (int j = i; j < maxn; j += i) {
            sum[j] += phi[i] * i / 2;
        }
    }
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n;
    init();
    cin >> t;
    while (t--) {
        cin >> n;
        cout << sum[n] * n << ENDL;
    }
    return 0;
}
线性筛再优化

显然上述代码不够优秀,因为根据狄利克雷卷积不难知道 f = i d ∗ φ f=id*\varphi f=idφ f ( n ) = ∑ d ∣ n φ ( d ) d f(n)=\sum_{d|n} \varphi(d)d f(n)=dnφ(d)d是积性函数,那么肯定是可以线性筛的,这里参考了 O I − w i k i OI-wiki OIwiki上的讲解,十分感谢!

  • 首先考虑 f ( p ) , p ∈ p r i m e s f(p),p\in primes f(p),pprimes,易得 f ( p ) = p ( p − 1 ) + 1 f(p)=p(p-1)+1 f(p)=p(p1)+1

  • 然后考虑 f ( p k ) f(p^k) f(pk),那么有 f ( p k ) = ∑ i = 0 k p i ∗ φ ( p i ) f(p^k)=\sum_{i=0}^k p^i*\varphi(p^i) f(pk)=i=0kpiφ(pi)
    根据欧拉函数的性质:对于素数 p p p,若 n % i = = 0 n\%i==0 n%i==0,那么 φ ( p n ) = p ∗ φ ( n ) \varphi(pn)= p*\varphi(n) φ(pn)=pφ(n)
    那么就有 f ( p k ) = ∑ i = 0 k p 2 k − 1 ( p − 1 ) f(p^k)=\sum_{i=0}^kp^{2k-1}(p-1) f(pk)=i=0kp2k1(p1)
    同理得 f ( p k + 1 ) = f ( p k ) + p 2 k + 1 ( p − 1 ) f(p^{k+1})=f(p^k)+p^{2k+1}(p-1) f(pk+1)=f(pk)+p2k+1(p1)

  • 对于线性筛中 p ∤ i p \nmid i pi的情况,显然为 f [ i ∗ p ] = f [ i ] ∗ f [ p ] f[i*p]=f[i] *f[p] f[ip]=f[i]f[p]

  • 对于线性筛中 p ∣ i p | i pi的情况,令 i = x ∗ p c , g c d ( x , p c ) = 1 i=x*p^c,gcd(x,p^c)=1 i=xpc,gcd(x,pc)=1,可得 f [ i ∗ p ] = f [ x ] ∗ f [ p c + 1 ] ① f[i*p]=f[x]*f[p^{c+1}]① f[ip]=f[x]f[pc+1]
    然后又根据 f [ i ] = f [ x ] ∗ f [ p c ] ② f[i]=f[x]*f[p^c]② f[i]=f[x]f[pc] ① − ② ①-② 有:
    f [ i ∗ p ] − f [ i ] = f [ x ] ∗ p 2 c + 1 ( p − 1 ) ③ f[i*p]-f[i]=f[x]*p^{2c+1}(p-1)③ f[ip]f[i]=f[x]p2c+1(p1),那么同理可得:
    f [ i ] − f [ i / p ] = f [ x ] ∗ p 2 c − 1 ( p − 1 ) ④ f[i]-f[i/p]=f[x]*p^{2c-1}(p-1)④ f[i]f[i/p]=f[x]p2c1(p1),联立 ③ , ④ ③,④ ,最终得到:
    f [ i ∗ p ] = f [ i ] + ( f [ i ] − f [ i / p ] ) ∗ p 2 f[i*p]=f[i]+(f[i]-f[i/p])*p^2 f[ip]=f[i]+(f[i]f[i/p])p2

时间复杂度为 O ( n ) O(n) O(n)


//
// Created by Happig on 2020/9/28
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e6 + 10;

vector<int> prime;
bitset<maxn> vis;
ll f[maxn];

void init() {
    f[1] = 1;
    for (int i = 2; i < maxn; i++) {
        if (!vis[i]) {
            prime.push_back(i);
            f[i] = 1LL * i * i - i + 1;
        }
        for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j]) {
                f[i * prime[j]] = f[i] * f[prime[j]];
            } else {
                f[i * prime[j]] = f[i] + (f[i] - f[i / prime[j]]) * prime[j] * prime[j];
                break;
            }
        }
    }
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n;
    init();
    cin >> t;
    while (t--) {
        cin >> n;
        cout << (f[n] + 1) * n / 2 << ENDL;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值