之前學習杜教篩的時候,把 φ 和 μ 的前綴和兩道題做過之後就沒有再深入學習…NOI D1T3 看出來是杜教篩但是不會推只好做一個被唾棄的暴力選手…越來越覺得自己數論姿勢太naive,所以補一補稍稍高端的知識。。。從刷經典題開始吧
前置技能(杜教篩)簡介:
我們記 f∘g 表示數論函數 f 和 g 的 Dirichlet 卷積,下同。
要計算 f(i) 的前綴和(記爲 F(i) ),考慮構造新的函數 g(i) ,使得 h=f∘g 的前綴和易於計算,那麼我們有:
∑i=1nh(i)=∑i=1n∑ab=if(a)×g(b)=∑ab≤nf(a)×g(b)=∑b=1ng(b)×F(⌊nb⌋)
這裏蘊含着一個關於 F(n) 的遞推式,如果能快速計算 ∑ni=1h(i) 和 g(i) 的值,同時預處理 i≤n23 的 F(i) ,那麼 F(n) 的計算可以被優化到 O(n23)
題意:給定 n ,計算 ∑ni=1∑nj=1(i,j) 。(n≤1011 )。
sol:
我們令 A(n)=∑ni=1(i,n) ,則答案等於 2×∑ni=1A(i)−∑ni=1i ,這個分 i<j , i=j , i>j 討論就能得出。
現在的問題是:如何快速計算 A(n) 的前綴和?
考慮化簡 A(n) ,枚舉 (i,n) ,我們有:
A(n)=∑d|nd×φ(nd)
顯然這是個積性函數,並且知道它是 id 和 φ 的 Dirichlet 卷積,因爲我們有:
φ=id∘μ
所以
id∘φ=id∘id∘μ
id∘φ∘1=id∘id
如果能夠快速計算 id∘id 的前綴和,那麼套用杜教篩,就能快速算出 A(n) 的前綴和。
記 A(n) 的前綴和爲 S(n) ,id∘id 的前綴和爲 H(n)
H(n)=∑i=1ni⌊ni⌋(⌊ni⌋+1)
這個顯然是可以分塊計算的。
使用杜教篩,我們總共需要計算 O(n√) 項 S(i) ,可以使用線性篩預處理出來 i≤n23 的S(i) 。對於每一個 i>n23 的 S(i) ,我們 O(i√) 地計算它對應的 H(i) 。因爲我們計算 S(i) 時需要枚舉 O(i√) 個 更小的 S(j) ,而計算 H(i) 的時間消耗也是 O(i√) 。所以暴力計算這些 H(i) 不會使複雜度的階升高,可以保證總的時間複雜度爲 O(n23)
題外話:用這種做法交上去,最慢的點大概需要 2.3s 左右。A掉之後我去觀摩了一下 1s 的神牛們的寫法,發現非常 excited :
注意到:
S(n)=∑i=1n∑j|ij×φ(ij)=∑ij≤ni×φ(j)=∑i=1ni∑j=1⌊ni⌋φ(j)
於是按照 ⌊ni⌋ 分塊計算一個 φ(i) 的前綴和就好了。。。我真是思博啊。。。
題意:給定 n ,計算 ∑ni=1∑nj=1[i,j] 。(n≤1011 )。
sol :
同樣的,考慮計算 ∑ni=1[i,n] ,記爲 A(n) ,同樣的枚舉 (i,n) :
A(n)=n∑d|n∑(i,nd)=1i
注意到 n>1 時,”小於n 且與n 互質的數” 的和爲 n×φ(n)2 ,對於 n=1 的情況特殊討論,上式又可以簡化爲:
A(n)=n2∑d|nd×φ(d)+n2
後一項的前綴和即爲 n×(n+1)4 ,以下討論式子前一項的做法:
記 f(n)=n∑d|nd×φ(d) ,f(n) 的前綴和爲 F(n) 。考慮如何計算 F(n)
把 n “分配” 到和式中去,我們有:
f(n)=∑ab=na2φ(a)×b
注意到這是 id2⋅φ 和 id 的 Dirichlet 卷積,我們進一步地推導:
注意到 (id2⋅φ)∘id2=id3 ,即:
∑ab=na2φ(a)×b2=n2∑a|nφ(a)=n3
那麼我們有:
id2∘(id2⋅φ)∘id=id3∘id
同樣地,我們記 id3∘id 的前綴和爲 H(n) ,那麼:
H(n)=∑i=1i∑j=1⌊nd⌋j3
這個也可以分塊計算,同上一題的複雜度分析,這道題我們也能得到一個 O(n23) 的做法。