zoukankan      html  css  js  c++  java
  • 整除分块

    目录

    目录地址

    上一篇

    下一篇


    整除函数

    我们定义下取整函数 (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) )的用从左往右更方便(因为代码更好记)

  • 相关阅读:
    CodeForces666E Forensic Examination
    #46. 【清华集训2014】玄学
    #207. 共价大爷游长沙
    BZOJ4259残缺的字符串
    [六省联考2017]分手是祝愿
    BZOJ2616PERIODNI
    UVa 1363 Joseph's Problem (等差数列)
    UVa 1641 ASCII Area
    UVa 10213 How Many Pieces of Land? (组合数学 & 图论)
    UVa 1640 The Counting Problem (数位DP)
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/12348576.html
Copyright © 2011-2022 走看看