数论分块
数论分块
数论分块就是一个小结论
对于一个常数n,和一个给定的数i(\(i <n\)),能使
的最大整数j(\(i\le j\le n\))为\(\lfloor{\frac{n}{\lfloor{\frac{n}{i}}\rfloor}}\rfloor{}\)
证明:设\(\lfloor{\frac{n}{i}}\rfloor=x\)
先需要证明\(\lfloor{\frac{n}{j}}\rfloor=x\)
一方面
另一方面
因此
又因为
因此,当\(j=\lfloor{\frac{n}{\lfloor{\frac{n}{i}}\rfloor}}\rfloor{}+1\)时不满足条件
所以我们得到了\(\lfloor{\frac{n}{i}}\rfloor\)所在块的右端点\(j=\lfloor{\frac{n}{\lfloor{\frac{n}{i}}\rfloor}}\rfloor{}\)
好了,我们已经知道了这个结论了,那我们如何使用呢?
数论分块可以计算一些关于\(\sum_{i=1}^nf(i)\lfloor{\frac{n}{i}}\rfloor\)的一些和式问题
我们只需要将数分成多个块,对于每个块,左端点为l,右端点为\(r=\lfloor{\frac{n}{\lfloor{\frac{n}{i}}\rfloor}}\rfloor{}\),这样可以减少时间复杂度。
例题
题意:
[CQOI2007]余数求和
题目描述
给出正整数 \(n\) 和 \(k\),请计算
思路:
我们可以将\(k \bmod i\)化简一下
\(k \bmod i=k-\lfloor{\frac{k}{i}}\rfloor\)
代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
int n,m;
ll read()
{
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
int main()
{
ll res;
n = read();
ll k = read();
ll l = 1, r;
res = n * k;
while(l <= n)
{
if(l > k)
{
break; //i大于k时直接结束
}
else r = min(k / (k / l),(ll)n);//注意右端点不能超过n!
res -= (r-l+1) * (k / l) *(l+r) / 2;
l = r+1;//更新左端点
}
cout << res;
return 0;
}