数论分块
通常用来解决 (sum_{i=1}^nlfloorfrac{n}{i} floor) 这种问题。
我们代入 n = 10。
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
(lfloorfrac{n}{i} floor) | 10 | 5 | 3 | 2 | 2 | 1 | 1 | 1 | 1 | 1 |
可以看到后面有连续的 (i), (lfloorfrac{n}{i} floor) 相同。呈现块状分布
每个块的起点 (l) 和终点 (r) 满足规律:(r=lfloorfrac{n}{lfloorfrac{n}{l} floor} floor)
而 (lfloorfrac{n}{i} floor) 的有效取值只有 (O(sqrt n)) 个,求解 (sum_{i=1}^nlfloorfrac{n}{i} floor) 就可以快速的实现了。
int ans=0;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
ans+=(r-l+1)*(n/l);
}
例题
P2261 [CQOI2007]余数求和
给出正整数 n 和 k,请计算
[G(n, k) = sum_{i = 1}^n k mod i
]
其中 (kmod i) 表示 k 除以 i 的余数。
化简给出的式子 (k mod i=k-lfloorfrac{k}{i} floor imes i)
[egin{aligned}
G(n, k) &= sum_{i = 1}^n k mod i\
&=sum_{i=1}^n(k-lfloorfrac{k}{i}
floor imes i)\
&=n imes k-sum_{i=1}^n(lfloorfrac{k}{i}
floor imes i)
end{aligned}
]
对于 (sum_{i=1}^n(lfloorfrac{k}{i} floor imes i)),每个块我们求出 ([l,r]) 的和 乘 当前块的 (lfloorfrac{k}{i} floor)
累加起来即可
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
int main()
{
ll n, k;
scanf("%lld%lld", &n, &k);
ll ans = 0;
for (ll l = 1, r; l <= min(n, k); l = r + 1) {
r = min(n, k / (k / l));
ans += (l + r) * (r - l + 1) / 2 * (k / l);
}
printf("%lld
", n * k - ans);
return 0;
}
P1403 [AHOI2005]约数研究
(f(i)) 表示 (i) 的约数的个数,给出 (n) ,请求出 (sum_{i=1}^{n}f(i))
枚举约数 (i),显然,区间 ([1,n]) 中,有 (lfloorfrac{n}{i} floor) 个数字是 (i) 的倍数。
答案即为 (sum_{i=1}^nlfloorfrac{n}{i} floor)