一、 基本思想
对于形如 \(\sum_{i=1}^n f(\lfloor \frac{n}{i}\rfloor)\) 的式子,先不考虑求 \(f(\lfloor \frac{n}{i}\rfloor)\) 的复杂度,则若采用朴素的解法,时间复杂度为 \(O(n)\)。
简便起见,我们令 \(f(\lfloor\frac{n}{i}\rfloor)=\lfloor \frac{n}{i}\rfloor\)。
打表找规律可以发现,\(\lfloor \frac{n}{i}\rfloor\) 的取值并没有 \(n\) 个,且在一定区域内相等,呈块状阶梯分布。
对于每一个块,假设它的左端点为 \(l\),右端点为 \(r\),对于 \(\forall i\in[l,r]\),\(\lfloor\frac{n}{i}\rfloor\) 的值都相等。那么我们只需要求出,每一个块的左端点和右端点,以及块内的值即可。
二、 具体实现
Part 1. 块的数量
对于任意整数 \(i\) 满足 \(1\leq i\leq n\),\(\lfloor\frac{n}{i}\rfloor\) 最多只有 \(2\sqrt{n}\) 个不同的值。因为:
-
当 \(i\leq \sqrt{n}\) 时,\(i\) 只有 \(\sqrt{n}\) 种选择,故 \(\lfloor\frac{n}{i}\rfloor\) 至多只有 \(\sqrt{n}\) 个不同的值。
-
当 \(i>\sqrt{n}\) 时,\(\lfloor\frac{n}{i}\rfloor < \sqrt{n}\),故 \(\lfloor\frac{n}{i}\rfloor\) 也至多只有 \(\sqrt{n}\) 个不同的值。
综上所述,对于 \(i=1\sim n\),\(\lfloor\frac{n}{i}\rfloor < \sqrt{n}\) 由不超过 \(2\sqrt{n}\) 个块组成。
Part 2. 块的左右端点
对于任意一个 \(i\)(\(i\leq n\)),我们需要找到一个最大的 \(x\)(\(i\leq x\leq n\)),使得 \(\lfloor\frac{n}{i}\rfloor=\lfloor\frac{n}{x}\rfloor\)。
此时 \(x=\lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor\)。如下所述:
-
显然 \(x\leq n\),考虑证明 \(x\geq i\)。
证明:因为 \(\lfloor\frac{n}{i}\rfloor\leq \frac{n}{i}\),所以 \(\lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor \geq \lfloor\frac{n}{\frac{n}{i}}\rfloor=\lfloor i\rfloor=i\)。则 \(i\leq \lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor =x\)。证毕。 -
不妨设 \(k=\lfloor\frac{n}{i}\rfloor\),考虑证明当 \(\lfloor\frac{n}{x}\rfloor=k\) 时,\(x\) 的最大值为 \(\lfloor\frac{n}{k}\rfloor\)。
证明:\(\lfloor\frac{n}{x}\rfloor=k\),等价于 \(k\leq \frac{n}{x}<k+1\),那么 \(\frac{1}{k+1}<\frac{x}{n}\leq \frac{1}{k}\),则 \(\frac{n}{k+1}<x\leq \frac{n}{k}\)。又因为 \(x\) 为整数,所以,\(x_{\max}=\lfloor\frac{n}{k}\rfloor\)。证毕。
每一块 \(i\in[x,\lfloor\frac{n}{\lfloor \frac{n}{x} \rfloor}\rfloor]\) 中 \(\lfloor \frac{n}{i}\rfloor\) 的值都等于 \(\lfloor\frac{n}{x}\rfloor\)。
对于每一个块,假设它的左端点为 \(l\),则它的右端点为 \(\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor\),并且块内元素都为 \(\lfloor\frac{n}{l}\rfloor\)。
注意:有时需要考虑 \(\lfloor \frac{k}{l}\rfloor=0\) 的情况。
时间复杂度:\(\mathcal{O}(\sqrt{n})\)。
#include<bits/stdc++.h> #define int long long using namespace std; int n,ans; signed main(){ scanf("%lld",&n); for(int l=1,r=0;l<=n;l=r+1) //l 为块的左端点,r 为块的右端点 r=n/(n/l),ans+=(r-l+1)*(n/l); //块的大小为 r-l+1,块内元素的值都为 n/l 下取整,则整个块的元素之和为 (r-l+1)*(n/l) printf("%lld\n",ans); return 0; }
Expand. 二维整除分块
求 \(\sum_{i=1}^{\min(n,m)} \lfloor\frac{n}{i}\rfloor \lfloor\frac{m}{i}\rfloor\)。
此时可将代码中 r=n/(n/l) 替换成 r=min(n/(n/l),m/(m/l))。
#include<bits/stdc++.h> #define int long long using namespace std; int n,m,ans; signed main(){ scanf("%lld%lld",&n,&m); for(int l=1,r=0;l<=min(n,m);l=r+1){ r=min(n/(n/l),m/(m/l)); ans+=(r-l+1)*(n/l)*(m/l); } printf("%lld\n",ans); return 0; }
三、例题
1. Luogu P2261 [CQOI2007]余数求和
题目大意:给出正整数 \(n\) 和 \(k\),求 \(\sum_{i=1}^n k \bmod i\) 的值。\(1\leq n,k\leq 10^9\)。
Solution:
注意到 \(k\bmod i=k-\lfloor \frac{k}{i} \rfloor \times i\),所以 \(\sum_{i=1}^n k \bmod i=\sum_{i=1}^n k-\sum_{i=1}^n\lfloor \frac{k}{i} \rfloor \times i\)\(=n\times k-\sum_{i=1}^n\lfloor \frac{k}{i} \rfloor \times i\)。
考虑整除分块,对于每一个块,假设它的左端点为 \(l\),右端点为 \(r\),则对于 \(\forall i\in[l,r]\),\(\lfloor \frac{k}{i}\rfloor \times i=\lfloor \frac{k}{l}\rfloor \times i\),直接用等差数列求和公式计算即可。
注意 \(\lfloor \frac{k}{l}\rfloor=0\) 的情况。
#include<bits/stdc++.h> #define int long long using namespace std; int n,k,ans; signed main(){ scanf("%lld%lld",&n,&k); for(int l=1,r=0;l<=n;l=r+1) r=k/l==0?n:min(k/(k/l),n),ans+=(k/l)*((l+r)*(r-l+1)/2); //注意这里 k/(k/l) 可能会超过 n,所以需要取 min。 printf("%lld\n",n*k-ans); return 0; }
2. Luogu P3935 Calculating
题目大意:若 \(x\) 分解质因数结果为 \(x=p_1^{k_1}p_2^{k_2}\cdots p_n^{k_n}\),令 \(f(x)=(k_1+1)(k_2+1)\cdots (k_n+1)\),求 \(\sum_{i=l}^rf(i)\) 对 \(998244353\) 取模的结果。\(1\leq l\leq 10^{14},1\leq r\leq 1.6\times 10^{14},r-l>10^{14}\)。
Solution:
根据唯一分解定理,可得:\(n=p_1^{c_1}\times p_2^{c_2}\times \cdots \times p_k^{c_k}=\prod\limits_{i=1}^kp_i^{c_i}\)。
\(p_i^{c_i}\) 的约数有 \(p_i^0,p_i^1,\cdots,p_i^{c_i}\) 共 \(c_i+1\) 个,根据乘法原理,可得 \(n\) 的约数个数为 \(d(n)=\prod\limits_{i=1}^k (c_i+1)\)。
容易得出,\(f(n)\) 实际上就是 \(n\) 的约数个数。
令 \(S(n)=\sum_{i=1}^n f(i)\),则 \(\sum_{i=l}^r f(i)=S(r)-S(l-1)\)。
\(S(n)=\sum_{i=1}^n \sum_{d\mid i} 1=\sum_{d=1}^n\lfloor \frac{n}{d}\rfloor\)。然后就可以用整除分块求了。
#include<bits/stdc++.h> #define int long long using namespace std; const int mod=998244353; int l,r,ans; int query(int n){ //求 S(n) int ans=0; for(int l=1,r=0;l<=n;l=r+1) r=n/(n/l),ans=(ans+(r-l+1)*(n/l)%mod)%mod; return ans; } signed main(){ scanf("%lld%lld",&l,&r); printf("%lld\n",(query(r)-query(l-1)+mod)%mod); return 0; }
四、习题
- Luogu P2260 [清华集训2012]模积和