ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数学/数论专题-学习笔记:整除分块

2022-04-17 19:00:18  阅读:261  来源: 互联网

标签:lfloor 分块 数论 Big sum rfloor dfrac 整除


目录

1. 前言

整除分块,是一种数论的基础算法,必备知识,在很多题目中都有涉及但是题目很基础。

整除分块主要解决的是这样一类问题:求值:

\[\sum_{i=1}^{n}f(i)g(\Big\lfloor\dfrac{k}{i}\Big\rfloor) \]

其中已知 \(f\) 的前缀和或者能 \(O(1)\) 计算 \(f(r)-f(l)\)。

正常的计算方式是 \(O(n)\) 的,但是一些题里面该复杂度不能接受,此时就需要整除分块,整除分块能做到 \(O(\sqrt{n})\) 解决此类问题。

2. 详解

首先讨论这个问题的简化版本:

\[\sum_{i=1}^{n}\Big\lfloor\dfrac{k}{i}\Big\rfloor \]

通过打表可以发现,对于 \(\Big\lfloor\dfrac{k}{i}\Big\rfloor\),容易有一个较大的区间 \([l,r]\),满足 \(\forall i,j \in [l,r],\Big\lfloor\dfrac{k}{i}\Big\rfloor=\Big\lfloor\dfrac{k}{j}\Big\rfloor\),下面定义一个满足上述条件的区间的值为 \(i \in [l,r],\Big\lfloor\dfrac{k}{i}\Big\rfloor\)。

也就是说有连续段是相同的,因此整除分块需要考虑如何快速处理出这些区间,最好的方式当然就是将区间处理完之后,不同的区间的值不同。

其实数学上已经证明了一些东西,这里不进行证明,直接将结论拉过来用:

  1. 这样的区间只有 \(O(\sqrt{n})\) 个。
  2. 对于一个区间 \([l,r]\),满足 \(r=\Big\lfloor\dfrac{k}{\lfloor\frac{k}{i}\rfloor}\Big\rfloor\)。

因此我们可以考虑从 \(l=1\) 开始,根据上述结论 2 算出 \(r\),而 \([l,r]\) 的值显然是 \((r-l+1) \times \Big\lfloor\dfrac{k}{l}\Big\rfloor\),然后 \(l\leftarrow r+1\),又根据结论 1,复杂度为 \(O(\sqrt{n})\)。

解决这个问题之后,回到我们原本的问题:

\[\sum_{i=1}^{n}f(i)g(\Big\lfloor\dfrac{k}{i}\Big\rfloor) \]

同样的,我们考虑处理出所有 \([l,r]\),满足 \(\forall i,j \in [l,r],\Big\lfloor\dfrac{k}{i}\Big\rfloor=\Big\lfloor\dfrac{k}{j}\Big\rfloor\),下面定义 \([l,r]\) 的值是 \(\sum_{i=l}^{r}f(i)g(\Big\lfloor\dfrac{k}{i}\Big\rfloor)\)。

这里先将这些区间存下来,记作 \([l_i,r_i]\),然后我们注意到此时的 \(\Big\lfloor\dfrac{k}{i}\Big\rfloor\) 是一个常数,可以直接将 \(g(\Big\lfloor\dfrac{k}{i}\Big\rfloor)\) 提出去,此时 \([l_i,r_i]\) 对答案的贡献就变成了这样:

\[g(\Big\lfloor\dfrac{k}{l_i}\Big\rfloor)\sum_{i=l_i}^{r_i}f(i) \]

然后因为可以快速计算 \(f(r)-f(l)\),所以照样可以 \(O(\sqrt{n})\) 解决。

代码里面注意两个点:

  1. 大于 \(n\) 的区间右端点要变成 \(n\)。
  2. 当 \(\Big\lfloor\dfrac{k}{l}\Big\rfloor\) 为 0 的时候 \(r=n\)。

通用 Code:

n = Read(), k = Read(); ans = 0;
for (int l = 1, r; l <= n; l = r + 1)
{
	if (k / l == 0) r = n;
	else r = Min(k / (k / l), n);
	++cnt; Left[cnt] = l, Right[cnt] = r;
}
for (int i = 1; i <= cnt; ++i)
	ans += (f(Left[i], Right[i]) * g(k / Left[i]));

其中 \(f(l,r)\) 计算 \(\sum_{i=l}^{r}f_i\),\(g(i)\) 就是算 \(g(i)\)。

接下来看道例题:P2261 [CQOI2007]余数求和

这道题给出 \(n,k\),要求:

\[\sum_{i=1}^{n}k \bmod i \]

首先将 \(\bmod\) 拆掉,将式子变成这样:

\[\sum_{i=1}^{n}(k-i \times \Big\lfloor\dfrac{k}{i}\Big\rfloor) \]

然后发现这个 \(k\) 可以提出去,变成这样:

\[nk-\sum_{i=1}^{n}(i \times \Big\lfloor\dfrac{k}{i}\Big\rfloor) \]

我们只需要处理后面这个式子,对应到整除分块上,发现 \(f(i)=i,g(\Big\lfloor\dfrac{k}{i}\Big\rfloor)=\Big\lfloor\dfrac{k}{i}\Big\rfloor\),满足 \(O(1)\) 计算 \(f(r)-f(l)\) 的条件,因此可以整除分块求后面这个式子,做完了。

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:P2261 [CQOI2007]余数求和
	Date:2022/2/18
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 1e5 + 5;
int n, k, Left[MAXN], Right[MAXN], cnt;
LL ans = 0;

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = sum * 10 + (ch ^ 48);
	return sum * fh;
}
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }

int main()
{
	n = Read(), k = Read(); ans = 1ll * n  * k;
	for (int l = 1, r; l <= n; l = r + 1)
	{
		if (k / l == 0) r = n;
		else r = Min(k / (k / l), n);
		++cnt; Left[cnt] = l, Right[cnt] = r;
	}
	for (int i = 1; i <= cnt; ++i)
		ans = ans - 1ll * (k / Left[i]) * 1ll * (Left[i] + Right[i]) * (Right[i] - Left[i] + 1) / 2;
		// 这里通过直接推式子代替了 f 和 g 函数
	printf("%lld\n", ans); return 0;
}

3. 总结

整除分块将形如 \(\sum_{i=1}^{n}f(i)g(\Big\lfloor\dfrac{k}{i}\Big\rfloor)\) 的式子化为了多个形如 \(g(\Big\lfloor\dfrac{k}{l_i}\Big\rfloor)\sum_{i=l_i}^{r_i}f(i)\) 的式子,其中 \(f(r)-f(l)\) 能够快速计算,从而减少时间复杂度,降至 \(O(\sqrt{n})\)。

4. 参考资料

标签:lfloor,分块,数论,Big,sum,rfloor,dfrac,整除
来源: https://www.cnblogs.com/Plozia/p/16156787.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有