整除分块
Posted justinrochester
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整除分块相关的知识,希望对你有一定的参考价值。
目录
整除函数
我们定义下取整函数 (floor(x)=lfloor x floor) 表示不大于 (x) 的最小整数
另外,定义上取整函数 (ceil(x)=lceil x ceil) 表示不小于 (x) 的最小整数
例如:
(lfloor3.1 floor=lfloor3 floor=3)
(lceil3.1 ceil=lceil4 ceil=4)
我们可以得到两个很显然的性质:
(lfloor x floorleq x<lfloor x floor+1)
(lceil x ceil-1<xleq lceil x ceil)
整除函数的实现
#include<cmath>
...
x=floor(x);//x=ceil(x)
考虑除法情况下的整除函数实现:
x=n/m;//x=floor(n/m)
x=n/m+(n%m!=0)//x=ceil(n/m)
取商除法
对于自然数 (n,m,k,r(m eq 0,0leq r<m)) 若满足带余除法式:
(ndiv m=kcdots r)
则称呼 (n) 为被除数, (m) 为除数, (k) 为商, (r) 为余数
显然,我们可以得到:(kleq {nover m}={km+rover m}=k+{rover m}<k+{mover m}=k+1)
简单来写,就是 (kleq {nover m}<k+1)
这里有一个很巧妙的转化:考虑到 (kin N,lfloor{nover m} floorleq {nover m}<lfloor{nover m} floor+1)
因此 (k=lfloor{nover m} floor)
因为本人是 C++ 选手,因此,习惯性的将之写为 (k=n/m)
即本人表示一下:在本人后期的贴子中,可能出现的 (n/m) 即 (lfloor{nover m} floor) ,与 ({nover m}) 区分
商的个数
对于被除数 (n) ,它的商的个数一定不超过 ((2sqrt n+1)) 个
证明:
对于除数 (min Z_+)
若 (mleq sqrt n) ,则商 (n/m) 的取值个数一定不超过 (m) 的个数
因此商 (n/m) 的取值个数一定不超过 (sqrt n) 个
若 (m>sqrt n) ,则商 ({nover m}) 的取值范围为 ([0,sqrt n))
因此 (n/m) 的取值个数一定不超过该区间内的整数个数,因此不超过 ((sqrt n+1)) 个
两部分相加,因此,商的个数不超过 ((2sqrt n+1)) 个
而特殊的,如保证 (mleq n) ,则另外可保证商的个数不超过 (2sqrt n)
即扣除了商为 (0) 的情况
整除分块
(除数的范围为 (1)~(n) )
考虑到被除数 (n) 的商个数是 (O(sqrt n)) 级别的
因此当题目需要考虑的是 (forall iin Z_+,lfloor{nover i} floor) 时,我们可以通过枚举这 ((2sqrt n+1)) 个商来实现
这样一来,我们可以把时间复杂度从 (O(n)) 降低至 (O(sqrt n))
我们先考虑:假设对于 (forall iin[l,r]igcap Z) 都有 (lfloor{nover l-1} floor eq lfloor{nover l} floor=lfloor{nover i} floor=lfloor{nover r} floor eq lfloor{nover r+1} floor)
( herefore displaystyle sum_{i=l}^rlfloor{nover i} floor=sum_{i=1}^rlfloor{nover l} floor=lfloor{nover l} floor(r-l+1))
那么,若我们能已知所有的 (l,r) 即可递推出:(displaystyle sum_{i=1}^nlfloor{nover i} floor=sumlfloor{nover l} floor(r-l+1))
注意到:(lfloor{nover l-1} floor eq lfloor{nover l} floor=lfloor{nover r} floor eq lfloor{nover r+1} floor)
因此,如果我们知道 (l) 或 (r) ,就能递推出下一个区间的 (r) 或 (l)
所以,我们选 (l) 或选 (r) 的关键就在于:
已知这个区间的 (l) 或 (r) ,能否推出另一个?如果能的话,就能根据推出的另一端,再推出下一个区间的 (l) 或 (r) ,然后循环至全部区间推出
我们先考虑已知左端点 (l) :
第一个 (l) 一定为 (1)
而对于区间 ([l,r]) ,由于 (forall iin[l,r]igcap Z) 都有 (lfloor{nover l-1} floor eq lfloor{nover l} floor=lfloor{nover i} floor=lfloor{nover r} floor eq lfloor{nover r+1} floor)
由 (r=max(i)) 得出 ({nover r}=min({nover i})geq lfloor{nover l} floor)
所以有 (rleq {nover lfloor{nover l} floor})
考虑到 (rin Z_+) 故 (r=lfloor{nover lfloor{nover l} floor} floor)
为避免混淆,我们记为 (r=n/(n/l))
再考虑已知右端点 (r) :
第一个 (r) 一定为 (n)
而同上可以得出 (l=min(i)) 从而有 ({nover l}=max({nover i})< lfloor{nover r} floor+1)
因此有 (l> {nover lfloor{nover r} floor+1})
同样考虑 (lin Z_+) 故 (l=lfloor{nover lfloor{nover r} floor+1}+1 floor=lfloor{nover lfloor{nover r} floor+1} floor+1)
为避免混淆,我们记为 (l=n/(n/r+1)+1)
整除分块的实现
由上面的推导可知
从左往右整除分块:
for(int l=1,r;l<=n;l=r+1){
int d=n/l;
r=n/d;
......
}
从右往左整出分块:
for(int r=n,l;r>=1;r=l-1){
int d=n/r;
l=n/(d+1)+1;
......
}
整出分块的一般形式
给定 (n,k) 所求式与 (forall iin[1,k]igcap Z,lfloor{nover i} floor) 有关
从左往右整出分块:
for(int l=1,r;l<=k;l=r+1){
int d=n/l;
r=Min(n/d,k);
......
}
从右往左整出分块:
for(int r=k,l;r>=1;r=l-1){
int d=n/r;
l=n/(d+1)+1;
......
}
同样的,考虑给定下界、同时给定上下界的整出分块
我们发现,实际上,给定上界的整出分块用从右往左更方便;其余情况(给定下界、给定上下界、范围为 (1)~(n) )的用从左往右更方便(因为代码更好记)
以上是关于整除分块的主要内容,如果未能解决你的问题,请参考以下文章