1. 程式人生 > 實用技巧 >[LOJ124] 除數函式求和 1 - 線性篩

[LOJ124] 除數函式求和 1 - 線性篩

Description

給定 \(n \le 10^7,k\),求 \(\sum_{i=1}^n \sigma_k (i)\),其中 \(\sigma_k(n)=\sum_{d|n} d^k\)

Solution

自然想到交換求和順序,即

\[\sum_{i=1}^n \sigma_k(i) =\sum_{i=1}^n \sum_{d|i} d^k=\sum_{i=1}^n[\frac n i]i^k \]

於是,不妨設 \(f(i)=i^k\),則我們可以對每個 \(f(i)\)\(O(\log n)\) 時間內計算,故總時間複雜度為 \(O(n\log n)\)。由於評測機很快,這樣已經能卡過去了。

考慮到 \(f(i)\) 是完全積性的,我們可以分出 \(i\) 對最小素因子 \(p_i\),利用線性篩計算所有合數的 \(f(i)\)。這樣複雜度為 \(O(n+\frac n {\log n} \log n)=O(n)\),可以接受。

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

#define int long long 
const int N = 10000005;
const int mod = 1e9+7;

int isp[N],vp[N],f[N],pcnt,n,k,ans;

int qpow(int p,int q)
{
    return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;
}

signed main()
{
    ios::sync_with_stdio(false);

    cin>>n>>k;

    for(int i=2;i<=n;i++)
    {
        isp[i]=1;
    }
    for(int i=2;i<=n;i++)
    {
        if(isp[i])
        {
            vp[++pcnt]=i;
            f[i]=qpow(i,k);   
        }
        for(int j=1;j<=pcnt&&vp[j]*i<=n;j++)
        {
            isp[i*vp[j]]=0;
            f[i*vp[j]]=f[i]*f[vp[j]]%mod;
            if(i%vp[j]==0) break;
        }
    }
    for(int i=1;i<=n;i++)
    {
        ans=(ans+(n/i)*f[i]%mod)%mod;
    }
    cout<<(ans+n)%mod<<endl;
    //system("pause");
}