博客園同步
原題鏈接
前置知識:
莫比烏斯反演,線性篩,狄利克雷卷積,整除分塊。
簡要題意:
T 組詢問,求:
i=1∑nμi
i=1∑nϕi
其中,T≤10,n<231.
首先,你需要知道這道題目的 O(Tnn) 算法,O(Tn) 算法,O(T+n) 算法。
顯然,O(Tnn) 是暴力,O(Tn) 是一個個篩,O(T+n) 是預處理 線性篩,一個都無法通過。
這題的瓶頸在於,我們 並不需要一個個求出 μ 和 ϕ,可以不拘泥於此。
想想:我們之前求 ∑i=1n⌊in⌋ 的時候,之前手玩 莫比烏斯反演 的時候,甚至有一次玩到 6 個 ∑ 還是一一解決了。走過這麼多困難,切掉了這麼多紫題,這一題怎麼就不行了呢?
事實告訴人們,題目從來都是人做出來的。
我們考慮幾個簡單的卷積:
μ∗I=ϵ,ϕ∗I=Id,μ∗Id=ϕ
好,根據這些,我們開始了偉大的探索。
假設我們要求一個 積性函數 f,可以先不直接求,搞出另一個 g,首先考慮怎麼求出 f∗g 的前綴和,那樣就可以求出 ∑f.
根據 莫比烏斯反演 的性質可得:
i=1∑n(f∗g)(i)
=i=1∑nd∣i∑f(d)×g(di)
=d=1∑ng(d)×i=1∑⌊dn⌋f(i)
假設 f 的前綴和爲 S,則:
=d=1∑ng(d)S(⌊dn⌋)
現在我們要求的是 S(n).
g(1)S(n)=d=1∑ng(d)S(⌊dn⌋)−d=2∑ng(d)S(⌊dn⌋)
顯然,因爲 g(1)S(n) 是 d=1 的答案,考慮前綴和相減即可。
此時可以得到:
g(1)S(n)=d=1∑n(f∗g)(d)−d=2∑ng(d)S(⌊dn⌋)
那麼顯然:
S(n)=g(1)∑d=1n(f∗g)(d)−∑d=2ng(d)S(⌊dn⌋)
此時你會說了,好,這個式子怎麼求呢?
這個式子確實難求,但關鍵是,g 函數是我們自己定的,我們可以定一個 g 使得 g 和 f∗g 的前綴和都非常好算。
好算?聯繫積性函數的性質,我們想到了:I 和 ϵ 都是積性函數,前綴和也非常的簡單。後面一堆我們可以整除分塊。
先不管怎麼定 g,僞代碼大概長這樣:
inline ll sieve(ll n) {
ll ans=calc(n);
for(ll i=2,r;i<=n;i=r+1) {
r=(n/(n/i));
ans-=(sum(r) - sum(i-1)) * sieve(n/i);
} return ans;
}
顯然你會發現,這裏有遞歸部分。
遞歸?這東西,顯然會把很多東西重複計算。
因此我們需要 記憶化。
到這裏,杜教篩 的樣子漸成,我們已經得到了其中的 60%.
下面的難點在於確定 g.
入手題目。
f=ϕ,令 g=I,則 f∗g=Id,此時可以解決。
f=μ,令 g=I,則 f∗g=ϵ,此時也可以解決。
好了,現在的問題在於,這東西的時間複雜度如何?
你發現無法通過,因爲,杜教篩必須要初始化一部分數據。
假設我們將 n≤k 的情況的答案都初始化一遍。那麼,經過分析(具體證明) 可得,這樣的時間複雜度是:
O(k+kn)
你會發現,只要 O(k) 不出問題,k 越大越好。
顯然,k 和 kn 成反比,相等時和最小。(反比例函數知識)
所以,k=n32 時,時間複雜度達到 O(n32),是比較優的。
時間複雜度: O(Tn32).
實際得分:100pts.
博主的代碼太醜,不想拿出來了。以後會重構代碼發出來的。