数论分块

数论分块可以在O(\(\sqrt(n)\))的时间内计算出形如 \(\sum_{i=L}^{R}k\%i\) 的式子
\(k\%i = k-(k/i)*i,而(k/i)的不同的个数大约有2\sqrt(n)个\),因此可以优化时间复杂度

一个有用的结论
枚举i:\(若\lfloor n/i \rfloor=\lfloor n/j \rfloor,且i\leq j,那么 j \in [ i , n/(n/i)]\)
枚举k:\(i \in [n/(k+1)+1, n/k ]\)

例题1
https://www.luogu.com.cn/problem/P2261

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,k;


int main(){
	cin>>n>>k;
	ll ans=0;
	if(n>k){
		ans=1ll*k*(n-k);
		n=k;
	}
	for(int i=1;i<=n;){
		ll mn=i;
		ll mx=k/(k/i);
		if(mx>n) mx=n; 
		ans+=((mx-mn+1)*k-(k/i)*(mn+mx)*(mx-mn+1)/2);
		i=mx+1;
	}
	cout<<ans<<endl;
	return 0;
}

例题2
https://ac.nowcoder.com/acm/contest/30896/A

长度为 n 的数组 a,下标从1开始,定义$ a[i]=n \% i$

有 m 组询问 {L,R},求 \(max_{i=L}^{R} a[i]\)

//区间最大值

#include<bits/stdc++.h>
using namespace std;
const int N = 1e8+10;
int n,m;
int a[N],pre[N];

int main(){
	cin>>n>>m;
	for(int i=1,l,r;i<=m;i++){
		scanf("%d%d",&l,&r);
		int imin=-1,imax=-1;
		int ans=0;
		for(int k=1;k*k<=n;k++){
			imin=n/(k+1)+1;
			imax=n/k;
			if(imax<l||imin>r) continue; 
			if(imin>=l&&imin<=r) ans=max(ans,n-k*imin);
			else if(imin<l){
				ans=max(ans,n-k*l);
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2022-03-27 00:39  starlightlmy  阅读(56)  评论(0)    收藏  举报