整除分块栗子

  • 例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";	
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章