数论分块
数论分块可以在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;
}

浙公网安备 33010602011771号