整除分块详解

@ 前言

彩笔作者最开始以为是真分块。。。

作者接触到这玩意是因为这道题:luogu2261 [CQOI2007]余数求和

题目简洁明了,一看就是推式子的题:

给出正整数 \(n\)\(k\),请计算

\(G(n, k) = \sum_{i = 1}^n k \bmod i\)

其中 \(k\bmod i\) 表示 \(k\) 除以 \(i\) 的余数。

$ 1\leq n,k\leq10^9$

首先暴力+小优化(\(n>k\) 的时候直接 \(ans+=(n-k)*k\)) 尝试了一波,可以,没想到居然水了 81 分 (正解吃**去吧)

然后开始推式子:

\[\begin{align*} ans&=\sum_{i=1}^n k\bmod i =\sum_{i=1}^nk-i*\lfloor\frac{k}{i}\rfloor\\ &=n *k-\sum_{i=1}^ni*\lfloor\frac{k}{i}\rfloor \end{align*} \]

嗯。。然后@#¥%……&*。

然后就不知道了,于是看了一下题解发现了有个东西叫做整除分块

(话说我这样算不算同时写了一篇题解)

A 入门

终于进入正题了

由前言可以知道,整除分块是用来解决快速求这样 \(\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor\) 的式子的一种方法

这个式子显然可以 \(O(n)\) 计算,也显然会T掉,所以我们需要加快

通过理性分析暴力打表可以发现这样一个特征:(下面是当 \(n=20\) 时候的情况)

| i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |16|17|18|19|20|
| ---------------------------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
| \(\lfloor\frac{20}{i}\rfloor\) | 20 | 10 | 6 | 5 | 4 | 3 | 2 | 2 | 2 | 2 | 1 | 1 | 1 | 1 | 1 |1|1|1|1|1|

(markdown好像炸了)

即: 表中同样的值会连续出现,成块状分布,所以从1到 \(n\) 的数组表可根据数值划分为不同的分块,且分块数远远小于 \(n\)

1.首先证明“分块数远远小于\(n\) ” ,也就是说,\(\lfloor\frac{n}{i}\rfloor\) 取值很少。
证:对于 \(i≤\sqrt n\) , 只有 \(\sqrt n\) 种,对于 \(i>\sqrt n\) ,\(\frac{n}{i}<\sqrt n\) ,也只有 \(\sqrt n\) 种取值,共计 \(2\sqrt n\) 种取值

现在还有一个问题是找到分块的点,如果我们可以 \(O(1)\) 找到,那么这个算法时间复杂度将会是优秀的 \(O(\sqrt n)\)

2.设 \(\lfloor\frac n i\rfloor\)\(\lfloor\frac {n}{i^{\prime}}\rfloor\) 相等,则 \(i^{\prime}\) 的最大值为 \(\lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor\)

证:设 \(\lfloor\frac{n}{i}\rfloor=k\) ,有 \(ki+p=n,1\le p\le i\)

\(\lfloor\frac{n}{i+d}\rfloor=k\) ,有 \(k(i+d)+p^{\prime}=n\)

可以得到 \(p^{\prime}=p-kd\) ,所以当 \(p^{\prime}=0\) 时有 \(d_{max}=\lfloor\frac{p}{k}\rfloor\) ,于是:

\[\begin{align*} i^{\prime}&=i+d_{max}=i+\lfloor\frac p k\rfloor\\ &=i+\lfloor\frac{n\bmod i}{\lfloor\frac n i\rfloor}\rfloor=i+\lfloor\frac{n-\lfloor\frac n i\rfloor i}{\lfloor\frac n i\rfloor}\rfloor\\ &=\lfloor i+\frac{n-\lfloor\frac n i\rfloor i}{\lfloor\frac n i\rfloor}\rfloor=\lfloor \frac{\lfloor\frac n i\rfloor i}{\lfloor\frac n i\rfloor}+\frac{n-\lfloor\frac n i\rfloor i}{\lfloor\frac n i\rfloor}\rfloor\\ &=\lfloor\frac{n}{\lfloor\frac{n}{i}\rfloor}\rfloor \end{align*} \]

原命题得证,此算法时间复杂度 \(O(\sqrt n)\)

当然还有更直接简明的证明方式:

\[\left\{ \begin{aligned} k & = \lfloor\frac ni\rfloor \\ i^{\prime} & =\max i,ik\le n \\ \end{aligned} \right. \]

有:

\[i^{\prime}=\lfloor\frac nk\rfloor=\lfloor\frac{n}{\lfloor\frac ni\rfloor}\rfloor \]

代码如下:

int ans=0;
for(int l=1,r;l<=n;l=r+1){
	r=n/(n/l);
	ans+=(r-l+1)*(n/l);
}
printf("%d\n",ans);

自动向下取整的啦

B 进阶

1.有时候,可能推出来的式子不一定就是一个很裸的整除分块,可能会与某些积性函数相乘,如:\(\phi\) 等这时候,我们就需要对这些函数统计一个前缀和。因为,每当我们使用整除分块跳过一个区间的时候,其所对应的函数值也跳过了一个区间。所以此时,就需要乘上那一个区间的函数值。

2.(公式太难打,就手写咯)

字丑勿喷,其实是换元法的应用

3.那向上取整呢?

我们知道:如果无法整除,那么 \(\lceil\frac ni\rceil=\lfloor\frac ni\rfloor+1\) ,而在整除时二者相等

所以我们不加1,我们加上 \(\frac{i-1}{i}\) 可以避免问题

所以最后得到的式子是:(呜呜呜,老老实实打公式)

\(i^{\prime}=\lfloor\frac{n-1}{k-1}\rfloor=\lfloor\frac{n-1}{\lfloor\frac{n+i-1}{i}\rfloor-1}\rfloor\)

C 参考

https://www.cnblogs.com/peng-ym/p/8661118.html

https://www.cnblogs.com/0xfffe/p/9648943.html

posted @ 2021-02-13 17:02  _Famiglistimo  阅读(509)  评论(1编辑  收藏  举报