整除分塊栗子

  • 例1
    在一些數論題中,經常會碰到類似這樣的式子:
    i=1nni\sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor

這個式子看似需要O(n)的時間複雜度計算,但實際上並不需要,因爲有時候對於不同的iini\left \lfloor \frac{n}{i} \right \rfloor的結果是一樣的,所以可以通過整除分塊來得到一個O(n)O(\sqrt{n})的算法。

整除分塊:

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n,ans=0;
	cin>>n;
	for(int l=1,r;l<=n;l=r+1){
    	r=n/(n/l);
    	ans+=(r-l+1)*(n/l);
	}
	cout<<ans<<"\n";	
} 

再來看一個例子。

  • 例2
    i=1nxni\sum_{i=1}^{n}x\left \lfloor \frac{n}{i} \right \rfloor
    這個式子怎麼算更快呢?知道l,rl,r的值後,把xx,用等差數列求和算出來,其餘部分分塊就好了。
#include<bits/stdc++.h>
using namespace std;

int main(){
	int n,ans=0;
	cin>>n;
	for(int l=1,r;l<=n;l=r+1){
    	r=n/(n/l);
    	ans+=(l+r)*(r-l+1)/2 *(n/l);//l,r的等差數列求和 首項加尾項乘以項數除以2 
	}
	cout<<ans<<"\n";	
} 

如果是這樣呢?
i=1nx2ni\sum_{i=1}^{n}x^{2}\left \lfloor \frac{n}{i} \right \rfloor
還是用相同的方法算,換一個計算公式而已,我們把等差數列求和換成平方數列求和的公式。

#include<bits/stdc++.h>
using namespace std;

int cal(int x){
	return x*(x+1)*(2*x+1)/6;
} 

int main(){
	int n,ans=0;
	cin>>n;
	for(int l=1,r;l<=n;l=r+1){
    	r=n/(n/l);
    	ans+=(cal(r)-cal(l-1))*(n/l);	
	}
	cout<<ans<<"\n";	
} 
  • 例3
    可能在做題目的過程中你還會碰到這樣的式子
    k=1nkxnx\sum^{n}_{k=1}\sum^{n}_{k|x}x
    就是求kk[1,n][1,n]內的所有倍數和。如果正常算這個式子要O(nlog(n))O(n*log(n))的時間。但其實這個求和也可以在O(n)O(\sqrt n)的時間內實現。
    分析:
    對於每個kk,n裏會存在nk\left \lfloor \frac{n}{k} \right \rfloorkk的倍數,而且等差,公差爲kk。所以式可以寫成
    k=1nk(nk+1)nk2\sum^{n}_{k=1}\frac{k(\left \lfloor \frac{n}{k} \right \rfloor+1)\left \lfloor \frac{n}{k} \right \rfloor}{2}
    可用整除分塊,快速求出。
#include<bits/stdc++.h>
using namespace std;


int main(){
	int n,ans1=0,ans2=0;
	cin>>n;
	for(int k=1;k<=n;k++)
		for(int j=1;j*k<=n;j++)
			ans1+=j*k;		
			
	for(int l=1,r;l<=n;l=r+1){
    		r=n/(n/l);
    		ans2+=(l+r)*(r-l+1)/2 *(1+n/l)*(n/l)/2;	
	}
	cout<<ans1<<" "<<ans2<<"\n";	
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章