整除分块(数论分块)

对于一个式子,在一个区间内具有相同的值,我们可以把它看成一个块,例如[n/i]([ ]表示向下取整,对应程序就是n/i),对于[1,n]的任何一个数k,它所在的块的右边界是n/(n/k),证明过程如下(图片源自b站董晓算法)

then,我们来看一道题目

 对于[sqrt(i)]也有块的性质,块的计算公式为r = ((ll)std::sqrt(l) + 1) * ((ll)std::sqrt(l) + 1);(ll是long long的意思,强制类型转换)

但是两个块的边界并不相同,所以可以考虑对边界取并集,使之成为一个新的分块,具体操作是取到两个块的所有边界,将边界进行排序.最后根据块的边界,两个数列分别对应的值求出答案

using ll = long long;

int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	ll n, ans = 0;
	std::cin >> n;
    //取两个块的边界
	ll l = 1, r = 0;
	std::vector<int>k;
	while (r < n) {
		r = n / (n / l);
		if(r<=n)k.push_back(r);
		l = r + 1;
	}
	if (k.back() != n)k.push_back(n);
	l = 1, r = 0;
	while (r <= n) {
		r = ((ll)std::sqrt(l) + 1) * ((ll)std::sqrt(l) + 1);
		if (r <= n)k.push_back(r-1);
		l = r + 1;
	}
    //k是新的块
	ll m = k.size();
	std::sort(k.begin(), k.end());
	l = 0;
    //根据新的块计算值
	for (int i = 0; i < m; i++) {
		r = k[i];
		if (l == r)continue;
		ll p = n / k[i], q = std::sqrt(k[i]);
		ans = ans + p * q*(r-l);
		l = r;
	}
	std::cout << ans;
	return 0;
}

<think>好的,我现在需要帮助用户理解整除分块算法的概念和实现方法。首先,我得回顾一下自己关于整除分块的知识,确保准确无误。 整除分块,我记得是用来快速计算形如$\sum_{i=1}^n \lfloor \frac{n}{i} \rfloor$这样的和的算法。因为当i增大时,$\lfloor \frac{n}{i} \rfloor$的值会分成多个连续的区间,每个区间内的值相同。例如,当n=10时,i=1到10对应的$\lfloor 10/i \rfloor$分别是10,5,3,2,2,1,1,1,1,1。可以看出,很多连续的i对应的结果相同,比如i=5和6时结果都是2,i从7到10都是1。整除分块就是利用这一点,将相同值的区间分块处理,减少计算次数,提高效率。 接下来,我需要解释这个算法的核心思想。用户提供的引用里提到,整除分块的关键是找到这些连续区间的右端点r。对于每个左端点l,对应的r应该是最大的整数,使得$\lfloor \frac{n}{l} \rfloor = \lfloor \frac{n}{r} \rfloor$。根据数学推导,这个r通常是$\lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor$。不过要注意不能超过n本身。 然后是实现方法。代码模板的大致流程是:初始化l为1,然后循环计算每个块的r,累加该块的贡献(即块的长度乘以该块的值),然后将l更新为r+1,直到l超过n。这个过程需要确保r不超过n,避免数组越界。用户提供的引用中的代码示例可以借鉴,但要注意语法是否正确。例如,用户引用中的代码用了min({n, n/(n/l)}),这里可能需要调整,因为n/(n/l)可能不是整数,或者应该用整数除法处理。正确的做法可能是r = min(n, n // (n // l)),在Python中需要用整除符号//,而用户提供的代码可能是C++的语法。 然后需要考虑如何将这些步骤用中文清晰地解释出来,结构要符合用户的要求:分步骤说明,包含概念、数学原理、实现步骤和示例。同时,引用用户提供的三个参考资料,正确标注引用标识。例如,在说明分块思想时引用[^1],在代码部分引用[^2],在数学公式部分引用[^3]。 可能遇到的问题包括如何正确推导r的值,以及如何处理边界情况。例如,当l超过n时循环终止,或者当n/l的商为0时的情况。不过通常当l超过n时,循环会自然结束,因为在循环条件中已经检查了l <= n。此外,每个块的贡献是(r - l + 1)乘以当前的值,即$\lfloor \frac{n}{l} \rfloor$,这部分需要明确说明。 最后,生成相关问题。根据用户提供的示例,问题应围绕整除分块的应用、优化、复杂度分析以及与其它算法的比较等方面展开。比如,如何分析时间复杂度,整除分块的优化方法,实际应用场景等。 总结一下,回答的结构应该是: 1. 概念:简要介绍整除分块是什么,解决什么问题。 2. 数学原理:解释为什么值会分块,如何找到每个块的右端点。 3. 实现步骤:分步骤说明算法流程,给出代码示例。 4. 示例分析:用具体数字举例说明。 5. 相关问题:提出几个相关问题供用户进一步学习。 需要确保所有数学公式使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$。同时检查引用标识是否正确添加,如引用[1]、[2]、[3]分别对应用户提供的三个参考资料。</think>### 整除分块算法详解 #### 1. 核心概念 整除分块是一种用于快速计算形如 $\sum_{i=1}^n \lfloor \frac{n}{i} \rfloor$ 的和的算法。其核心思想是**将相同的 $\lfloor \frac{n}{i} \rfloor$ 值划分为连续区间(块)**,通过分块计算减少运算次数,将时间复杂度从 $O(n)$ 优化到 $O(\sqrt{n})$[^1]。 #### 2. 数学原理 - **分块依据**:当 $i$ 增大时,$\lfloor \frac{n}{i} \rfloor$ 的值会形成多个连续区间。例如,$n=10$ 时: $$i=1 \to 10,\quad i=2 \to 5,\quad i=3 \to 3,\quad i=4-5 \to 2,\quad i=6-10 \to 1$$ - **右端点公式**:对于每个块的左端点 $l$,右端点 $r$ 满足: $$r = \min\left(n, \left\lfloor \frac{n}{\lfloor n/l \rfloor} \right\rfloor\right)$$ 该公式保证 $\lfloor \frac{n}{l} \rfloor = \lfloor \frac{n}{r} \rfloor$。 #### 3. 实现步骤 1. **初始化**:左端点 $l=1$,总和 $ans=0$。 2. **循环分块**: - 计算当前块的右端点 $r$。 - 累加当前块的贡献:块长度 $(r-l+1)$ × 块值 $\lfloor \frac{n}{l} \rfloor$。 - 更新左端点 $l = r + 1$。 3. **终止条件**:当 $l > n$ 时结束循环。 **代码实现(Python)**: ```python def division_block(n): ans = 0 l = 1 while l <= n: q = n // l r = n // q if q != 0 else n # 防止除零错误 r = min(r, n) ans += (r - l + 1) * q l = r + 1 return ans ``` #### 4. 示例分析 以 $n=10$ 为例: - **块1**:$l=1$,$q=10$,$r=1$,贡献 $1×10=10$。 - **块2**:$l=2$,$q=5$,$r=2$,贡献 $1×5=5$。 - **块3**:$l=3$,$q=3$,$r=3$,贡献 $1×3=3$。 - **块4**:$l=4$,$q=2$,$r=5$,贡献 $2×2=4$。 - **块5**:$l=6$,$q=1$,$r=10$,贡献 $5×1=5$。 **总和**:$10+5+3+4+5=27$[^2]。 #### 5. 复杂度分析 - **时间复杂度**:$O(\sqrt{n})$。因为 $\lfloor \frac{n}{i} \rfloor$ 的取值最多有 $2\sqrt{n}$ 种(证明数论知识)。 - **空间复杂度**:$O(1)$,仅需常数级变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值