數論分塊
數論分塊
定義:
數論分塊可以在 \(O \sqrt{n}\) 的時間裡計算一些有除法下取整的和式。
主要是 將 \(\frac{n}{d}\) 相同的數一起同時計算。
定理:
定理 \(1\):
\[a,b,c \in \mathbb{Z}, \lfloor \frac{a}{bc} \rfloor =\lfloor \frac{\lfloor\frac{a}{b}\rfloor}{c}\rfloor \]證明:
\[\frac{a}{b}=\lfloor \frac{a}{b} \rfloor+r \]帶入將該值代入原式中即可。
定理 \(2\):
\[|\lfloor \frac{n}{d} \rfloor| \leq \lfloor 2\sqrt{n} \rfloor \]其中,\(n \leq d\)
證明:
對於 \(d \leq/\geq \lfloor \sqrt{n} \rfloor\) ,對應式子都只有 \(\lfloor \sqrt{n} \rfloor\) 種取值。
過程:
首先,為了好寫部落格,定義 \(| x |=\lfloor x \rfloor\) (小懶不算懶)
考慮含有 \(|\frac{n}{i}|\) 的求和式子。
對於任意一個 \(i \leq n\) 我們需要找到一個最大的 \(j\),使得 \(|\frac{n}{i}|=|\frac{n}{j}|\)
此時 \(j=|\frac{n}{|\frac{n}{i}|}|\)
顯然 \(i\leq j\leq n\) .
我們設 \(k=|\frac{n}{i}|\) ,證明: 當 \(|\frac{n}{j}|=k\) 時, \(j\) 的最大值為 \(|\frac{n}{k}|\)
\[k \leq \frac{n}{j} < k+1 \]兩邊同時取倒數,再乘上 \(n\),易得:
\[\frac{n}{k+1}<j \leq \frac{n}{k} \]因為 \(j \in \mathbb{Z}\) ,所以 \(j_{max}=|\frac{n}{k}|\)
利用上述結論,我們每次以 \([i,j]\) 為一塊,分塊求和即可。
例題:
題意:
計算 \(ans=\sum_{i=1}^n (k \mod i)\)
分析:
顯然,這個式子可以轉化成:
\[n \times k-\sum_{i=1}^n i \lfloor \frac{k}{i} \rfloor \]考慮後面的式子:
一開始 \(l=1\)
我們設 \(\lfloor \frac{k}{l} \rfloor=t\) ,分兩種情況:
-
\(t \not ={0},r=min(\lfloor \frac{k}{t} \rfloor,n)\);
-
\(t= 0 ,r=n\).
然後如果沒有到邊界,直接 \(l=r+1\) ,再次計算 \(\lfloor \frac{k}{l} \rfloor\) 即可。
此時,這一段的答案就是:
\(\frac{(l+r)}{2}\)( 原式子中 \(i\) 的中間值) \(\times (r-l+1)\) (該區間長度) \(\times \lfloor \frac{k}{l} \rfloor\) (對應的值)
最後跳出迴圈,輸出即可。
程式碼:
// 「luogu P2261」[CQOI2007]餘數求和
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
int l,r,ans;
signed main(){
cin>>n>>k;
ans=k*n;
for(int l=1;l<=n;l=r+1){
int now=k/l;
if(now!=0) r=min(k/now,n);
else r=n;
ans-=(r-l+1)*(l+r)/2*(k/l);
}
cout<<ans<<endl;
system("pause");
return 0;
}