整除分块

· · 个人记录

整除分块

在解决 {\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}

# 例题:[P2261 [CQOI2007]余数求和](https://www.luogu.com.cn/problem/P2261) ## 题目描述 给出正整数 $n$ 和 $k$,请计算 $$G(n, k) = \sum_{i = 1}^n k \bmod i$$ 其中 $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$。 **参考程序:** ```cpp #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}$ . 结合刚才学的结合分块,相信大家都会做了。 -------------