【題解&杜教篩總結】51Nod1238 最小公倍數和V3

前置知識:杜教篩。(不會點這裏)。

大片公式預警!!!

題意

i=1nj=1nlcm(i,j)\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\text{lcm}(i,j)。(1n10101\leq n\leq 10^{10}

題解

簡單回顧

開始之前,先來回顧一下幾個和杜教篩有關的式子(定義*爲數論函數的狄利克雷卷積):
I(n)=1,id(n)=n,ϵ(n)=[n=1]μI=ϵ(1)φI=id(2)μid=φ(3) \begin{aligned}&I(n)=1, \quad id(n)=n, \quad\epsilon(n)=[n=1]\\&\mu*I=\epsilon \qquad (1) \\&\varphi*I=id \qquad (2)\\&\mu*id=\varphi \qquad (3)\\\end{aligned}
(1)(1)可以推出dnμ(d)=[n=1]\sum\limits_{d|n}\mu(d)=[n=1]

(2)(2)可以推出dnφ(d)=n\sum\limits_{d|n}\varphi(d)=n

(3)(3)則經常用於μ\muφ\varphi的互相轉化。

正式開始

看到這種題,上來就先暴力化式子。(除法默認下去整)
i=1nj=1nlcm(i,j)=i=1nj=1nij(i,j)=d=1n1di=1ndj=1ndd2ij[(i,j)=d](先枚舉gcd)=d=1ndi=1ndj=1ndijx(i,j)μ(x)(由(1)式可得)=d=1ndx=1ndx2μ(x)i=1ndxj=1ndxij \begin{aligned}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\text{lcm}(i,j)&=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\frac{ij}{(i,j)} \\&=\sum_{d=1}^{n}\frac{1}{d}\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}}d^2ij\cdot[(i,j)=d] &\text{(先枚舉gcd)}\\&=\sum_{d=1}^{n}d\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{n}{d}}ij\sum_{x|(i,j)}\mu(x) &\text{(由(1)式可得)}\\&=\sum_{d=1}^{n}d \sum_{x=1}^\frac{n}{d}x^2\mu(x) \sum_{i=1}^{\frac{n}{dx}}\sum_{j=1}^{\frac{n}{dx}}ij\end{aligned}
接下來,設T=dxT=dxg(x)=i=1xj=1xij=(i=1xi)2=n2(n+1)24g(x)=\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{x}ij=(\sum\limits_{i=1}^{x}i)^2=\frac{n^2(n+1)^2}{4}。於是我們先枚舉TT,把g(x)g(x)帶進去,就可以接着化上面那個式子了。
d=1ndx=1ndx2μ(x)i=1ndxj=1ndxij=T=1ng(nT)xTx2μ(x)Tx \begin{aligned}\sum_{d=1}^{n}d \sum_{x=1}^\frac{n}{d}x^2\mu(x) \sum_{i=1}^{\frac{n}{dx}}\sum_{j=1}^{\frac{n}{dx}}ij&=\sum_{T=1}^{n}g(\frac{n}{T})\sum_{x|T}x^2\mu(x)\cdot\frac{T}{x}\end{aligned}
然後我們發現前面g(nT)g(\frac{n}{T})可以除法分塊,關鍵在於如何求出後面這一部分的前綴和。

所以令F(n)=xnx2μ(x)nxF(n)=\sum\limits_{x|n}x^2\mu(x)\cdot\frac{n}{x}A(n)=n2μ(n)A(n)=n^2\mu(n)B(n)=nB(n)=n,那麼F=ABF=A*B

BB看起來已經很簡單了,所以我們嘗試用構造一個CC,讓ACA*C變得簡單一些,這樣就可以用杜教篩求前綴和了(很明顯FF是積性函數)。

我們發現AA中的x2x^2讓我們很不舒服,所以我們考慮C(x)=x2C(x)=x^2,這樣在卷積的時候可以把x2x^2給消掉。
(AC)(n)=xnx2μ(x)(nx)2=n2xnμ(x)=n2[n=1](由之前的推論可得)=[n=1]AC=ϵ \begin{aligned}(A*C)(n)&=\sum_{x|n}x^2\mu(x)(\frac{n}{x})^2\\&=n^2\sum_{x|n}\mu(x)\\&=n^2[n=1] &\text{(由之前的推論可得)}\\&=[n=1]\\\therefore A*C=\epsilon\end{aligned}
誒,這下看起來簡單多了。
FC=(AC)B=ϵB=B(FC)(n)=n \begin{aligned}\therefore F*C&=(A*C)*B\\&=\epsilon*B\\&=B\\\therefore (F*C)&(n)=n\end{aligned}
S(n)S(n)F(n)F(n)的前綴和,然後套上杜教篩的式子:
C(1)S(n)=i=1n(FC)(n)i=2nC(i)S(ni)S(n)=n(n+1)2i=2ni2S(ni) \begin{aligned}C(1)S(n)&=\sum_{i=1}^n(F*C)(n)-\sum_{i=2}^nC(i)S(\frac{n}{i})\\即\quad S(n)&=\frac{n(n+1)}{2}-\sum_{i=2}^{n}i^2S(\frac{n}{i})\end{aligned}
那麼只要能夠線性求出前幾百萬個SS就可以直接杜教篩了。

觀察F(n)=xnx2μ(x)nx=nxnxμ(x)F(n)=\sum\limits_{x|n}x^2\mu(x)\cdot\frac{n}{x}=n\sum\limits_{x|n}x\mu(x),所以考慮線性篩出積性函數g(n)=xnxμ(x)g(n)=\sum\limits_{x|n}x\mu(x)

我們想想線性篩的時候有那些情況需要考慮。

  1. nn爲質數時,g(n)=1μ(1)+nμ(n)=1ng(n)=1\cdot \mu(1)+n\cdot \mu(n)=1-n
  2. pp爲質數且與ii互質時,g(ip)=g(i)g(p)=g(i)(1p)g(i\cdot p)=g(i)\cdot g(p)=g(i)\cdot (1-p)
  3. pp爲質數且ii爲p的倍數時,由積性函數的性質可知,g(i)=g(p1a1)g(p2a2)g(i)=g(p_1^{a_1})\cdot g(p_2^{a_2})\cdots,那麼g(ip)g(i\cdot p)中,pp的指數至少是2,又因爲當指數a>1a>1g(pa)=g(pa1)+paμ(pa)=g(pa1)g(p^a)=g(p^{a-1})+p^a\cdot \mu(p^a)=g(p^{a-1}),則g(pa)=g(p)g(p^{a})=g(p),所以此時g(ip)=g(i)g(i\cdot p)=g(i)

考慮完這三種情況後,我們就可以線性篩出SS了。

代碼

#include <bits/stdc++.h>
#define ll long long
#define MAX 5000005
#define P 1000000007
using namespace std;

ll qpow(ll a, ll n){
    ll res = 1;
    while(n){
        if(n&1) res = res*a%P;
        a = a*a%P;
        n >>= 1;
    }
    return res;
}

const ll inv2 = (P+1)/2, inv6 = 166666668;

int cnt;
ll p[MAX], vis[MAX], f[MAX];
void init(int n){
    f[1] = 1;
    for(int i = 2; i <= n; i++){
        if(!vis[i]){
            p[++cnt] = i;
            f[i] = 1-i+P;
        }
        for(int j = 1; j <= cnt && p[j]*i <= n; j++){
            vis[p[j]*i] = 1;
            if(i%p[j] == 0){
                f[i*p[j]] = f[i];
                break;
            }
            f[i*p[j]] = f[i]*(1-p[j]+P)%P;
        }
    }
    for(int i = 1; i <= n; i++){
        f[i] = (f[i]*i%P+f[i-1])%P;
    }
}

ll sum(ll n){
    n %= P;
    return n*(n+1)%P*(2*n%P+1)%P*inv6%P;
}
ll g(ll n){
    n %= P;
    return n*(n+1)%P*n%P*(n+1)%P*inv2%P*inv2%P;
}

map<ll, ll> s;
ll S(ll n){
    if(n < MAX) return f[n];
    if(s.count(n)) return s[n];
    ll ans = (n+1)%P*(n%P)%P*inv2%P;
    for(ll l = 2, r; l <= n; l = r+1){
        r = n/(n/l);
        (ans -= (sum(r)-sum(l-1)+P)%P*S(n/l)%P) %= P;
        if(ans < 0) ans += P;
    }
    return s[n] = ans%P;
}

int main()
{
    init(MAX-1);
    ll n, ans = 0;
    cin >> n;
    for(ll l = 1, r; l <= n; l = r+1){
        r = n/(n/l);
        ll t = n/l, sum = (S(r)-S(l-1)+P)%P;
        ans = (ans+g(t)%P*sum%P)%P;
    }
    cout << ans << endl;

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章