数论分块

数论分块

算法简介

能够在 \(\mathcal{O(\sqrt{n})}\) 的时间复杂度内计算出含有 \(\sum\limits_{i = 1}^{n} \left \lfloor \frac{k}{i} \right \rfloor\) 等式子。

\(a_i = \left \lfloor \frac{k}{i} \right \rfloor\),其主要思想为显然存在若干段连续区间内 \(a_i\) 值相同,那么我们就可以将一个块内的 \(a_i\) 一起计算,可以做到 \(\mathcal{O(\sqrt{n})}\) 的时间复杂度。

举例说明:

\(k = 10\) 时,我们可以作出以下表格:

\(i\) \(1\) \(2\) \(3\) \(4 \sim 5\) \(6 \sim 10\)
\(\left \lfloor \frac{k}{i} \right \rfloor\) \(10\) \(5\) \(3\) \(2\) \(1\)

当然,也可以作出以下 \(y = \frac{10}{x}\) 的反比例函数图像。

可以看到,\(4 \sim 5\) 以及 \(6 \sim 10\) 这两个连续段内 \(a_i\) 的值均相同。

算法过程

我们考虑如何去求每个块内的值。

可以发现对于每个块,我们可以通过递推计算得到其块内的值 \(val\),以及整个块的左右端点 \(l,r\)

显然,其中 \(val = \left \lfloor \frac{k}{i} \right \rfloor,r = \left \lfloor \frac{k}{\left \lfloor \frac{k}{i} \right \rfloor} \right \rfloor\)\(l\) 的值可以根据上一次的 \(r' + 1\) 得来。

对于 \(r = \left \lfloor \frac{k}{\left \lfloor \frac{k}{i} \right \rfloor} \right \rfloor\),有证明:

显然 \(val = \left \lfloor \frac{k}{i} \right \rfloor\)\(val \leq \frac{k}{i}\),那么 \(i = \left \lfloor \frac{i}{\frac{k}{i}} \right \rfloor\)\(i \leq \left \lfloor \frac{k}{val} \right \rfloor\),所以 \(i\) 最大值即为 \(\left \lfloor \frac{k}{val} \right \rfloor = \left \lfloor \frac{k}{\left \lfloor \frac{k}{i} \right \rfloor} \right \rfloor\)

P2261 [CQOI2007] 余数求和

\[\begin{aligned} G(n,k) &= \sum\limits_{i = 1}^{n} k \bmod i \\ &= \sum\limits_{i = 1}^{n} k - \left \lfloor \frac{k}{i} \right \rfloor \cdot i \\ &= n \cdot k - \sum\limits_{i = 1}^{n} i \cdot \left \lfloor \frac{k}{i} \right \rfloor \end{aligned} \]

对于这道题,我们就每次统计一个块内 \(\sum i \cdot \left \lfloor \frac{k}{i} \right \rfloor\),根据以上式子,能够写出如下代码:

$\tt{Link}$
// prob: P2261
#include <bits/stdc++.h>

#define int long long
// #define ll long long
#define ull unsigned long long
#define db double
#define rep(i,l,r) for (int i = (l); i <= (int)(r); ++ i )
#define rep1(i,l,r) for (int i = (l); i >= (int)(r); -- i )
#define il inline
#define fst first
#define snd second
#define Yes puts("Yes")
#define No puts("No")
#define YES puts("YES")
#define NO puts("NO")
#define mk make_pair
#define pb emplace_back
#define sz(x) (int)(x.size())
#define all(x) x.begin(),x.end()
#define debug() puts("------------")
#define DEBUG() puts("/bx/bx/bx WaterSun")

using namespace std;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
const int N = 2e5 + 10,inf = 1e9,mod = 998244353;
const int base = 233;
int n,k;

il void read(int &x) {
	x = 0;
	short flag = true;
	char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') flag = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); }
	x *= flag;
}

il void solve() {
	//------------code------------
	read(n); read(k);
	int res = k * n;
	for (int l = 1,r; l <= n; l = r + 1 ) {
		if (k / l) r = min(n,k / (k / l));
		else r = n;
		res -= (l + r) * (r - l + 1) / 2 * (k / l);
	}
	printf("%lld\n",res);
}

signed main() {
	int t = 1;
	// read(t);
	while (t -- ) solve();
	return 0;
}
posted @ 2024-01-24 21:01  songszh  阅读(22)  评论(0编辑  收藏  举报