由於最近在學習莫比烏斯反演,發現整除分塊這個東西幾乎是很是必要的,由於是真的好用,能夠把一些須要\(O(n)\)的枚舉優化到$O(\sqrt n)html
什麼式子能夠用整除分塊呢?通常是這樣
\[\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor\]
咱們發現(打表或者是本身yy),對於一段連續的區間,\(\lfloor\frac{n}{i}\rfloor\)的值是不變的,那麼對於這一段區間,咱們就能夠跳過,\(O(1)\)計算出這一段區間的值c++
既然是分塊,那麼一塊的邊界是什麼呢?
首先,明確一點,首端點l是枚舉出來的,而末端點是\(n/(n/l)\)(感性理解一下)
而據dalao分析,時間複雜度是\(O(\sqrt n)\),有了這個範圍,咱們就能夠分塊了函數
inline void init (int ans=0) { for(int l=1,r,len;l<=n;l=r+1) { r=n/(n/l),len=r-l+1; ans+=len*(n/l); } }
不少時候,整除分塊是配合其餘一些函數來用的,such as \(\mu\),\(\phi\)...
當咱們的區間跳躍的時候,函數值也會跳躍,因此就要記得乘上這一段區間的函數值,這個時候就須要前綴和優化了
聽dalao說,有些噁心的題目會卡線性篩(T飛),這個時候就須要杜教篩,你問我杜教篩是什麼?問得好,我也不知道(逃~是真的,之後有時間再補吧)學習
放幾道整除分塊的例題
[AHOI2005]約數研究
[CQOI2007]餘數求和
洛谷P3935 Calculating
第一題沒什麼思路,直接預處理,在這裏放一下代碼優化
#include<bits/stdc++.h> #define il inline #define rg register #define lol long long #define Min(a,b) (a)<(b)?(a):(b) #define Max(a,b) (a)?(b)?(a):(b) using namespace std; void in(int &ans) { ans=0; char i=getchar(); while(i<'0' || i>'9') i=getchar(); while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar(); } int main() { int n,ans=0; in(n); for(rg int l=1,r,len;l<=n;l=r+1) { r=n/(n/l),len=r-l+1; ans+=n/l*len; } printf("%d\n",ans); }
第二題也是轉換一下題目要求的東西
\[a\mod b= a-\lfloor\frac{a}{b}\rfloor\times b\]
而後套一下等比數列求和就能夠了
注意一下邊界spa
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define il inline #define rg register #define lol long long #define Min(a,b) (a)<(b)?(a):(b) #define Max(a,b) (a)?(b)?(a):(b) using namespace std; void in(lol &ans) { ans=0; char i=getchar(); while(i<'0' || i>'9') i=getchar(); while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar(); } int main() { lol n,m;in(n),in(m); lol ans=n*m; for(rg lol l=1,r,len;l<=n;l=r+1) { if(m/l!=0) r=Min(m/(m/l),n); else r=n; len=(r-l+1); ans-=(m/l)*len*(l+r)/2; } printf("%lld\n",ans); }
第三題題解code
下面是配合莫比烏斯反演的題
[POI2007]ZAP-Queries 題解1
[SDOI2015]約數個數和 題解2
YY的GCD 題解3htm