整除分块
整除分块
在解决 \({\sum \limits_{i=1}^{n} \lfloor \frac{n}{i} \rfloor }\) 这样的问题时,如果用暴力计算,效率显然是十分低下的。
所以这里引入整除分块算法,仅用 \({O( \sqrt{n} )}\) 的复杂度。
下面以 \({n=8}\) 为例。
观察上面第二行的值,发现是逐步变小的,而且是一段一段相等的数。
于是乎,为了快速求和,我们就给它一段一段求和。
即:数值 乘上 个数。
可以发现每一种值都是 \({n}\) 的约数,所以这样的枚举不会超过 \({2\sqrt{n}}\) 个,那么它的复杂度也就有了保证。
复杂度:\({O(2\sqrt{n})}\)
参考代码:
long long get(long long n) {
long long ans=0;
for(long long l=1,r;l<=n;l=r+1) {
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
return ans;
}
证明:
先让 \({l}\) 为分块的左边界,那么这块的单个值 \({k= \lfloor \frac{n}{l} \rfloor}\) ,这样右边界 \({r}\) 就是值为 \({k}\) 的最大下标 \({i}\) ,也就是说要找满足 \({i \leqslant \frac{n}{k}}\) 的最大的 \({i}\) ,即 \({r=max(i)=\lfloor \frac{n}{k}}\) ,将 \({k}\) 代入,得到 \({r=\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor}\) 。
\({tip:}\) 只要 \({i}\) 取值 \({[l,r)}\) ,则 \({ r=\lfloor \frac{n}{\lfloor \frac{n}{i} \rfloor} \rfloor }\).
例题:P2261 [CQOI2007]余数求和
题目描述
给出正整数 \(n\) 和 \(k\),请计算
其中 \(k\bmod i\) 表示 \(k\) 除以 \(i\) 的余数。
输入格式
输入只有一行两个整数,分别表示 \(n\) 和 \(k\)。
输出格式
输出一行一个整数表示答案。
样例 #1
样例输入 #1
10 5
样例输出 #1
29
提示
样例 1 解释
\(G(10, 5)=0+1+2+1+0+5+5+5+5+5=29\)。
数据规模与约定
- 对于 \(30\%\) 的数据,保证 \(n , k \leq 10^3\)。
- 对于 \(60\%\) 的数据,保证 \(n, k \leq 10^6\)。
- 对于 \(100\%\) 的数据,保证 \(1 \leq n, k \leq 10^9\)。
参考程序:
#include<bits/stdc++.h>
using namespace std;
long long n,k,ans=0;
int main() {
cin>>n>>k;
ans=n*k;
for(long long l=1,r;l<=min(n,k);l=r+1) {
r=k/(k/l);
if(r>n) r=n;
ans-=(k/l)*((l+r)*(r-l+1)/2);
}
cout<<ans;
return 0;
}
题解:
首先我们要清楚: \({ a \% b = a - \lfloor \frac{a}{b} \rfloor \times b}\) .
所以原式可以化为:\({nk - \sum \limits_{i=1}^{n} \lfloor \frac{k}{i} \rfloor \times i}\) .
结合刚才学的结合分块,相信大家都会做了。